/* Copyright (c) KylinSoft  Co., Ltd. [2023].All rights reserved.
    [ostree-maintain-daemon] licensed under the [Your LICENSE].
    Author: [ZhangTingting]
    Create: [2024]
*/

#include "mm-log.h"
#include "mm-public.h"
#include <dbus/dbus-glib.h>
#include <dbus/dbus.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char *save_all = "all";
char *save_none = "none";

DBusConnection *connect_dbus()
{
    DBusError err;
    DBusConnection *connection;
    int ret;

    dbus_error_init(&err);

    g_info("mm-cli: Attempting to connect to the system D-Bus.");

    /* Connect to Bus*/
    connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
    if (dbus_error_is_set(&err)) {
        g_warning("mm-cli: Failed to connect to the system D-Bus: %s", err.message);
        dbus_error_free(&err);
        exit(1);
    } else {
        g_info("mm-cli: Successfully connected to the system D-Bus.");
    }

    return connection;
}

gboolean send_bool_type_method_call(DBusConnection *connection, const char *method_name, char *mode)
{
    DBusError err;
    DBusMessage *msg, *reply;
    DBusMessageIter iter;

    dbus_error_init(&err);

    msg = dbus_message_new_method_call("com.kylin.MaintainMode", "/com/kylin/MaintainMode/Object", "com.kylin.MaintainMode.interface", method_name);

    if (msg == NULL) {
        g_printerr("mm-cli: Message NULL");
        exit(1);
    }

    if (strcmp(method_name, "OpenMaintainMode") != 0 && strcmp(method_name, "GetBootMode") != 0) {
        dbus_message_iter_init_append(msg, &iter);
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &mode)) {
            g_warning("mm-cli: Failed to append integer argument to D-Bus message.");
            // fprintf(stderr, "Failed to append arguments\n");
            dbus_message_unref(msg);
            exit(1);
        }
    }

    reply = dbus_connection_send_with_reply_and_block(connection, msg, DBUS_TIMEOUT_INFINITE, &err);
    if (dbus_error_is_set(&err)) {
        g_warning("mm-cli: D-Bus method call failed: %s", err.message);
        // fprintf(stderr, "Method call failed: %s\n", err.message);
        dbus_error_free(&err);
        dbus_message_unref(msg);
        exit(1);
    }

    if (reply) {
        dbus_message_iter_init(reply, &iter);
        dbus_bool_t value;
        dbus_message_iter_get_basic(&iter, &value);

        dbus_message_unref(reply);
        dbus_message_unref(msg);
        return value;
    }

    dbus_connection_flush(connection);
    dbus_message_unref(msg);
    dbus_error_free(&err);
}

gchar *send_spring_type_method_call(DBusConnection *connection, const char *method_name)
{
    DBusError err;
    DBusMessage *msg, *reply;
    DBusMessageIter iter;
    gchar *value1 = NULL;

    dbus_error_init(&err);

    g_info("mm-cli: Fetching the current system status...");

    msg = dbus_message_new_method_call("com.kylin.MaintainMode", "/com/kylin/MaintainMode/Object", "com.kylin.MaintainMode.interface", method_name);

    if (msg == NULL) {
        g_printerr("mm-cli: Message NULL");
        exit(1);
    }

    reply = dbus_connection_send_with_reply_and_block(connection, msg, DBUS_TIMEOUT_INFINITE, &err);
    if (dbus_error_is_set(&err)) {
        fprintf(stderr, "Method call failed: %s\n", err.message);
        dbus_error_free(&err);
        dbus_message_unref(msg);
        exit(1);
    }

    if (reply) {
        dbus_message_iter_init(reply, &iter);
        if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
            dbus_message_iter_get_basic(&iter, &value1);

        } else {
            g_warning("mm-cli: Expected string but got type %c from D-Bus\n", dbus_message_iter_get_arg_type(&iter));
        }
        dbus_message_unref(reply);
    } else {
        g_warning("mm-cli: No reply from D-Bus\n");
    }

    dbus_connection_flush(connection);
    dbus_message_unref(msg);

    return value1;
}

static void usage(void)
{
    printf(
        "用法: mm-cli [OPTIONS...]\n"
        "OPTIONS\n"
        "  -s, --mms                        Check whether the current status is in maintain mode or normal mode\n"
        "  -o, --mmo                        Activate maintain mode\n"
        "  -c -a, --save-all                Deactivate maintain mode\n"
        "  -c -n, --save-none               Deactivate maintain mode\n"
        "\n");
}

int main(int argc, char *argv[])
{
    int c = 0;
    int k = 0;
    gboolean result1;
    gchar *result2;
    DBusConnection *connection;
    connection = connect_dbus();
    mm_log_init();

    if (connection == NULL) {
        g_warning("mm-cli: Failed to connect to DBus.");
        return -1;
    }

    g_info("mm-cli: DBus connection established successfully.");

    if (argc < 2) {
        printf("%s: No other parameters are provided\n", argv[0]);
        printf("Please try %s -h to get more information\n", argv[0]);
        return -1;
    }

    while ((c = getopt(argc, argv, "ocs::h")) != -1) {
        switch (c) {
        case 'o':
            if (geteuid() != 0) {
                g_info("mm-cli: mm-cli -o: Permission denied: Enter maintain mode as root\n");
                printf("Permission denied: Enter maintain mode as root\n");
                return -1;
            }
            if (argc > 2) {
                g_warning("mm-cli: mm-cli -o: Failed to open maintenance mode due to too many input parameters.");
                return -1;
            }
            g_info("mm-cli: The command 'mm-cli -o' has been executed successfully, and it is attempting to enable maintain mode...");
            result2 = send_spring_type_method_call(connection, "GetBootMode");
            if (strcmp(result2, "maintain") == 0) {
                send_bool_type_method_call(connection, "OpenMaintainMode", NULL);
                g_info("mm-cli: mm-cli -o: The current system is already in maintain mode and cannot be activated again.");
                printf("The current system is already in maintain mode and cannot be activated again\n");
            } else if (strcmp(result2, "normal") == 0) {
                send_bool_type_method_call(connection, "OpenMaintainMode", NULL);
                g_info("mm-cli: mm-cli -o: Maintain mode is activated.");
                printf("Maintain mode is activated \n");
            }
            break;
        case 's':
            if (argc > 2) {
                return -1;
            }
            g_info("mm-cli: The command 'mm-cli -s' has been executed successfully. Checking current boot mode...");
            result2 = send_spring_type_method_call(connection, "GetBootMode");
            if (strcmp(result2, "maintain") == 0) {
                g_info("mm-cli: Current mode is maintain.");
                printf("Maintain mode\n");
            } else {
                g_info("mm-cli: Current mode is normal.");
                printf("Normal mode\n");
            }
            break;
        case 'c':
            if (geteuid() != 0) {
                g_info("mm-cli: mm-cli -o: Permission denied: Exit maintain mode as root\n");
                printf("Permission denied: Exit maintain mode as root\n");
                return -1;
            }
            if (argc <= 2) {
                g_warning("mm-cli: Failed to turn off maintenance mode with 'mm-cli -c -a': Not enough input parameters.");
                printf("Please continue to input other parameters to confirm whether to save the installed packages\n");
                printf("Use %s -h for more information\n", argv[0]);
                // send_bool_type_method_call(connection, "CloseMaintainMode",NULL);
            }
            while ((k = getopt(argc, argv, "an")) != -1) {
                switch (k) {
                case 'a':
                    g_info("mm-cli: The command 'mm-cli -c -a' has been executed successfully, and all packages will be saved...");
                    result2 = send_spring_type_method_call(connection, "GetBootMode");
                    if (strcmp(result2, "normal") == 0) {
                        g_info("mm-cli: mm-cli -c -a: The current system is normal mode,cannot exit maintain mode.");
                        printf("The current system is normal mode,cannot exit maintain mode\n");
                    } else if (strcmp(result2, "maintain") == 0) {
                        gchar *ovl_dir_path_tmp = mm_get_ovl_dir_path();
                        if (ovl_dir_path_tmp == NULL) {
                            g_info("mm: get ovl dir path failed.");
                        } else {
                            gchar *ovl_dir_path = g_strstrip(ovl_dir_path_tmp);
                            gchar *etc_layer_path = g_strconcat(ovl_dir_path, ETC_LAYER_TMP_UPPER, NULL);
                            gchar *usr_layer_path = g_strconcat(ovl_dir_path, USR_LAYER_TMP_UPPER, NULL);
                            gchar *var_layer_path = g_strconcat(ovl_dir_path, VAR_LAYER_TMP_UPPER, NULL);
                            gsize etc_size = calculate_disk_usage(etc_layer_path);
                            gsize usr_size = calculate_disk_usage(usr_layer_path);
                            gsize var_size = calculate_disk_usage(var_layer_path);
                            gsize total_size = etc_size + usr_size + var_size;
                            g_free(etc_layer_path);
                            g_free(usr_layer_path);
                            g_free(var_layer_path);

                            char *space_size = print_human_readable_size(total_size);
                            if (space_size) {
                                printf("Retaining operation and maintenance data, with a pending data size of %s\n", space_size);
                                g_info("mm: Retaining operation and maintenance data, with a pending data size of %s.", space_size);
                                free(space_size);
                            }
                        }
                        free(ovl_dir_path_tmp);
                        printf("Processing conflict files, please be patient...\n");
                        g_info("mm: Processing conflict files, please be patient...");
                        send_bool_type_method_call(connection, "CloseMaintainMode", save_all);
                        g_info("mm-cli: mm-cli -c -a: Maintain mode has been deactivated with all packages saved.");
                        printf("Maintain mode has been deactivated\n");
                    }
                    break;
                case 'n':
                    g_info("mm-cli: The command 'mm-cli -c -n' has been executed successfully, and all packages will not be saved...");
                    result2 = send_spring_type_method_call(connection, "GetBootMode");
                    if (strcmp(result2, "normal") == 0) {
                        g_info("mm-cli: mm-cli -c -n: The current system is normal mode,cannot exit maintain mode.");
                        printf("The current system is normal mode,cannot exit maintain mode\n");
                    } else if (strcmp(result2, "maintain") == 0) {
                        send_bool_type_method_call(connection, "CloseMaintainMode", save_none);
                        g_info("mm-cli: mm-cli -c -n: Maintain mode has been deactivated without saving packages.");
                        printf("Maintain mode has been deactivated\n");
                    }
                    break;
                }
                break;
            }
            break;
        case 'h':
            usage();
            break;
        }
    }
    return 0;
}
