#include "systemoperation.h"

#include <iostream>

#include "assistant/message.h"
#include "utils/logger.h"
#include "utils/util.h"

DialogueData::CallSuccessIdentifier SystemOperation::callSystemControlInterface(
    const DialogueData::Intent& scenarioIntent) {
    DialogueData::CallSuccessIdentifier result{false, ""};

    if (scenarioIntent.name.empty()) {
        LOGE("Error: Invalid scenarioIntent");
        return result;
    }

    size_t pos = scenarioIntent.name.find("_");
    if (pos == std::string::npos) {
        return result;
    }
    std::string cmdName = scenarioIntent.name.substr(pos + 1);

    if (cmdName.empty()) {
        LOGE("Error: Invalid scenarioIntent");
        return result;
    }

    auto cmdType = systemCommandDiscrimination(cmdName);
    LOGD("系统控制命令为：{}", cmdName);
    switch (cmdType) {
        case SystemCommandType::systemControlWithSlot: {
            result.successRes = systemControlWithSlotHandle(cmdName, scenarioIntent.slots);
            return result;
        }
        case SystemCommandType::systemControlWithoutSlot: {
            result.successRes = systemControlWithoutSlotHandle(cmdName, scenarioIntent.slots);
            return result;
        }
        case SystemCommandType::thirdPartyAppControl: {
            result.successRes = thirdPartyAppControlHandle(cmdName, scenarioIntent.slots);
            return result;
        }
        case SystemCommandType::scheduleManagement: {
            result.successRes = scheduleManagementHandle(cmdName, scenarioIntent.slots);
        }
        case SystemCommandType::localDataSearch: {
            break;
        }
        default: {
            LOGE("未识别的系统控制命令");
            break;
        }
    }
    return result;
}

bool SystemOperation::callSourceUrlTool(int toolId) {
    const std::string appName = assistant::message::getAppNameFromSourceId(toolId);
    if (appName.empty()) {
        LOGE("App name is empty.");
        return false;
    }
    std::string cmd = "kylin-user-guide -A " + appName + " &";
    LOGI("cmd: {}.", cmd);
    // TODO 不安全
    system(cmd.c_str());
    return true;
}

DialogueData::CallSuccessIdentifier SystemOperation::callSystemScheduleManagementInterface(std::string inputContent) {
    DialogueData::CallSuccessIdentifier result;
    inputContent = formatJsonFromSchedulingInputContent(inputContent);
    Json::Value inputContentJson = util::stringToJson(inputContent);
    if (inputContentJson.isNull() || !inputContentJson.isObject() || !inputContentJson.isMember("日程主题") ||
        !inputContentJson.isMember("日程开始时间") || !inputContentJson.isMember("日程结束时间")) {
        result.successRes = false;
        LOGE("SystemScheduleManagement input content is invalid.");
        return result;
    }
    SchedulingInfo schedulingInfo = getSchedulingInfo(inputContentJson);
    if (schedulingInfo.script.empty()) {
        result.successRes = false;
        LOGE("SystemScheduleManagement input content is invalid.");
        return result;
    }
    if (!callDbusCalendar(schedulingInfo, SchedulingType::createSchedule)) {
        result.successRes = false;
        LOGE("Create Schedule failed");
        return result;
    }
    result.scheduleDate = std::to_string(schedulingInfo.startTime.year) + "-" +
                          std::to_string(schedulingInfo.startTime.month) + "-" +
                          std::to_string(schedulingInfo.startTime.day);
    LOGI("Return OpenSchedule date is{}", result.scheduleDate);
    result.successRes = true;
    return result;
}

std::string SystemOperation::formatJsonFromSchedulingInputContent(const std::string& input) {
    std::string formattedContent = input;
    if (formattedContent.substr(0, 7) == "```json") {
        formattedContent = formattedContent.substr(7);
    }
    if (formattedContent.substr(formattedContent.size() - 3, formattedContent.size()) == "```") {
        formattedContent = formattedContent.substr(0, formattedContent.size() - 3);
    }
    if (formattedContent.substr(0, 1) == "{" &&
        formattedContent.substr(formattedContent.size() - 1, formattedContent.size()) != "}") {
        formattedContent = formattedContent + "}";
    }
    if (formattedContent.substr(0, 1) != "{" &&
        formattedContent.substr(formattedContent.size() - 1, formattedContent.size()) == "}") {
        formattedContent = "{" + formattedContent;
    }
    return formattedContent;
}

DialogueData::CallSuccessIdentifier SystemOperation::callSystemToDoListInterface(const std::string& inputContent) {
    DialogueData::CallSuccessIdentifier result;
    std::vector<SchedulingInfo> schedulingInfos = getSchedulingInfo(inputContent);
    if (schedulingInfos.empty()) {
        result.successRes = false;
        return result;
    }
    int schedulingErrorCount = 0;
    for (auto info : schedulingInfos) {
        LOGI("To do list is {}", info.script);
        LOGI("Start time is {}年{}月{}日 {}:{}", info.startTime.year, info.startTime.month, info.startTime.day,
             info.startTime.hour, info.startTime.minute);
        LOGI("End time is {}年{}月{}日 {}:{}", info.endTime.year, info.endTime.month, info.endTime.day,
             info.endTime.hour, info.endTime.minute);
        if (!callDbusCalendar(info, SchedulingType::createSchedule)) {
            schedulingErrorCount += 1;
            LOGE("Create to-do list failed");
        }
    }
    if (schedulingErrorCount == schedulingInfos.size()) {
        result.successRes = false;
        return result;
    }
    result.scheduleDate = std::to_string(schedulingInfos.front().startTime.year) + "-" +
                          std::to_string(schedulingInfos.front().startTime.month) + "-" +
                          std::to_string(schedulingInfos.front().startTime.day);
    LOGI("Return OpenSchedule date is{}", result.scheduleDate);
    result.successRes = true;
    return result;
}

DialogueData::CallSuccessIdentifier SystemOperation::callSystemOpenScheduleInterface(const std::string& inputContent) {
    DialogueData::CallSuccessIdentifier result;
    SchedulingInfo openSchedulingTime;
    if (inputContent.empty()) {
        result.successRes = false;
        return result;
    }
    if (!parseTime(inputContent, openSchedulingTime.startTime, SchedulingType::openSchedule)) {
        result.successRes = false;
        LOGE("Invalid openSchedulingTime {}", inputContent.c_str());
        return result;
    }
    if (!callDbusCalendar(openSchedulingTime, SchedulingType::openSchedule)) {
        result.successRes = false;
        LOGE("Open Schedule failed");
        return result;
    }
    result.successRes = true;
    return result;
}

SystemCommandType SystemOperation::systemCommandDiscrimination(const std::string& name) {
    if (std::find(SystemCommandNamesWithoutSlot_.begin(), SystemCommandNamesWithoutSlot_.end(), name) !=
        SystemCommandNamesWithoutSlot_.end()) {
        return SystemCommandType::systemControlWithoutSlot;
    };

    if (std::find(SystemCommandNamesWithSlot_.begin(), SystemCommandNamesWithSlot_.end(), name) !=
        SystemCommandNamesWithSlot_.end()) {
        return SystemCommandType::systemControlWithSlot;
    };

    if (std::find(AppCommandNames_.begin(), AppCommandNames_.end(), name) != AppCommandNames_.end()) {
        return SystemCommandType::thirdPartyAppControl;
    };

    if (std::find(ScheduleManagementNames_.begin(), ScheduleManagementNames_.end(), name) !=
        ScheduleManagementNames_.end()) {
        return SystemCommandType::scheduleManagement;
    };

    if (name == "全局搜索") {
        return SystemCommandType::localDataSearch;
    }

    return SystemCommandType::typeNotRecognized;
}
bool SystemOperation::systemControlWithSlotHandle(const std::string& cmdName,
                                                  std::vector<DialogueData::Slot> slots) const {
    if (slots.empty()) {
        return false;
    }
    for (auto& slot : slots) {
        if (slot.values.empty()) {
            return false;
        }
        return systemControlOperation(cmdName, slot.values.front());
    }
    return false;
}
bool SystemOperation::systemControlWithoutSlotHandle(const std::string& cmdName,
                                                     std::vector<DialogueData::Slot> slots) const {
    std::string value = "default";
    return systemControlOperation(cmdName, value);
}

bool SystemOperation::scheduleManagementHandle(const std::string& cmdName,
                                               std::vector<DialogueData::Slot> slots) const {
    if (cmdName == "闹钟设置") {
        return systemControlOperation("闹钟设置", "打开");
    }
    return false;
}
bool SystemOperation::thirdPartyAppControlHandle(const std::string& cmdName,
                                                 const std::vector<DialogueData::Slot> slots) const {
    if (cmdName == "应用控制") {
        return systemAppLaunchOperation(slots);
    }
    if (cmdName == "多媒体-播放器控制") {
        return systemMediaPlayerOperation(slots);
    }
    if (cmdName == "多媒体-音乐播放") {
        return systemControlOperation("音乐播放", "打开");
    }
    if (cmdName == "多媒体-视频播放") {
        return systemControlOperation("视频播放", "打开");
    }
    return false;
}
bool SystemOperation::systemControlOperation(const std::string& cmdName, const std::string& value) const {
    return callCreateSystemApi(cmdName, value);
}
bool SystemOperation::systemAppLaunchOperation(const std::vector<DialogueData::Slot> slots) const {
    std::string cmdName = "应用控制";

    if (slots.size() == 0) {
        LOGE("应用控制命令缺少参数");
        return false;
    }

    std::string actionType = getAppActionType(slots);
    std::string appName = getAppName(slots);

    if (actionType.empty() || appName.empty()) {
        return false;
    }

    if (actionType == "open_store") {
        cmdName = "应用商店";
        appName = "打开";
    }
    if (appName == "放大镜") {
        return callDbusMagnifier();
    }
    if (appName == "日历") {
        SchedulingInfo time;
        std::time_t t = std::time(nullptr);
        std::tm* now = std::localtime(&t);
        time.startTime.year = now->tm_year;
        time.startTime.month = now->tm_mon;
        time.startTime.day = now->tm_mday;
        return callDbusCalendar(time, SchedulingType::openSchedule);
    }
    if (AppNamesMap_.count(appName) > 0) {
        appName = AppNamesMap_.at(appName);
    }
    return callCreateSystemApi(cmdName, appName);
}
bool SystemOperation::systemMediaPlayerOperation(const std::vector<DialogueData::Slot> slots) const {
    std::string cmdName = "";
    std::string value = "";
    if (slots.size() == 0) {
        LOGE("应用控制命令缺少参数");
        return false;
    }
    for (auto& slot : slots) {
        if (slot.name == "类型") {
            if (slot.values.front() == "音乐") {
                cmdName = "自带音乐播放器控制";
            };
        } else if (slot.name == "操作") {
            value = slot.values.front();
        }
    }
    return callCreateSystemApi(cmdName, value);
}
std::string SystemOperation::getAppActionType(const std::vector<DialogueData::Slot>& slots) const {
    for (const auto& slot : slots) {
        if (slot.name == "操作类型" && !slot.values.empty()) {
            std::string action = slot.values.front();
            if (action == "打开") {
                return "launch";
            }
            if (action == "更新" || action == "卸载" || action == "下载") {
                return "open_store";
            }
        }
    }
    return "";
}
std::string SystemOperation::getAppName(const std::vector<DialogueData::Slot>& slots) const {
    for (auto& slot : slots) {
        if (slot.name == "桌面应用名称") {
            if (not slot.values.empty()) {
                return slot.values.front();
            }
        }
    }
    return "";
}
bool SystemOperation::callCreateSystemApi(const std::string& cmdName, const std::string& value) const {
    auto cmd = apiPlatform_.createApi(cmdName, value);
    if (!cmd) {
        LOGE("创建api失败");
        return false;
    }
    if (cmd->execute()) {
        LOGD("API命令已执行");
        return true;
    }
    LOGE("Api命令执行失败");
    return false;
}

SchedulingInfo SystemOperation::getSchedulingInfo(const Json::Value& input) {
    SchedulingInfo schedulingInfo;
    schedulingInfo.script = input["日程主题"].asString();
    if (!parseTime(input["日程开始时间"].asCString(), schedulingInfo.startTime, SchedulingType::createSchedule)) {
        LOGE("Invalid SchedulingInfo: startTime format is not correct{}", input["日程开始时间"].asCString());
        return {};
    }
    if (!parseTime(input["日程结束时间"].asCString(), schedulingInfo.endTime, SchedulingType::createSchedule)) {
        LOGE("Invalid SchedulingInfo: endTime format is not correct{}", input["日程结束时间"].asCString());
        return {};
    }
    if (schedulingInfo.startTime.year == 0 || schedulingInfo.startTime.month == 0 ||
        schedulingInfo.startTime.day == 0 || schedulingInfo.endTime.year == 0 || schedulingInfo.endTime.month == 0 ||
        schedulingInfo.endTime.day == 0) {
        LOGE("Invalid SchedulingInfo {}-{}-{} {}:{}", schedulingInfo.startTime.year, schedulingInfo.startTime.month,
             schedulingInfo.startTime.day, schedulingInfo.endTime.year, schedulingInfo.endTime.month,
             schedulingInfo.endTime.day);
        return {};
    }
    LOGI("SchedulingInfo {}", schedulingInfo.script);
    LOGI("SchedulingInfo start time is {}-{}-{} {}:{}", schedulingInfo.startTime.year, schedulingInfo.startTime.month,
         schedulingInfo.startTime.day, schedulingInfo.startTime.hour, schedulingInfo.startTime.minute);
    LOGI("SchedulingInfo end time is {}-{}-{} {}:{}", schedulingInfo.endTime.year, schedulingInfo.endTime.month,
         schedulingInfo.endTime.day, schedulingInfo.endTime.hour, schedulingInfo.endTime.minute);
    return schedulingInfo;
}

std::vector<SchedulingInfo> SystemOperation::getSchedulingInfo(const std::string& input) {
    std::vector<SchedulingInfo> schedules;
    std::istringstream ss(input);
    std::string line, script;
    SchedulingTime startTime = {0, 0, 0, 0, 0}, endTime = {0, 0, 0, 0, 0};

    auto saveSchedule = [&]() {
        if (!script.empty() && startTime.year != 0) {
            schedules.push_back({script, startTime, endTime});
            script.clear();
            startTime = {0, 0, 0, 0, 0};
            endTime = {0, 0, 0, 0, 0};
        }
    };

    while (std::getline(ss, line)) {
        if (line.empty()) {
            saveSchedule();
        } else {
            if (line[0] == '#') {
                saveSchedule();
                parseTime(line.substr(2), startTime, SchedulingType::todoList);
                // 待办只生成当天日程，结束时间为当前时间
                endTime = startTime;
            } else {
                if (line != "- 无") {
                    if (line[0] == '-') {
                        line = line.substr(1);
                    }
                    script += line;
                }
            }
        }
    }
    saveSchedule();
    return schedules;
}

bool SystemOperation::parseTime(const std::string& input, SchedulingTime& time, const SchedulingType type) {
    std::istringstream ss(input);
    if (type == SchedulingType::todoList) {
        if (sscanf(input.c_str(), "%d年%d月%d日", &time.year, &time.month, &time.day)) {
            // 待办未指定时间采用9:00
            time.hour = 9;
            time.minute = 0;
            return true;
        }
        LOGE("Invalid date format in to do list: {}", input);
        return false;
    }
    if (type == SchedulingType::createSchedule) {
        if (sscanf(input.c_str(), "%d-%d-%d %d:%d", &time.year, &time.month, &time.day, &time.hour, &time.minute)) {
            return true;
        }
        return false;
    }
    if (type == SchedulingType::openSchedule) {
        if (sscanf(input.c_str(), "%d-%d-%d", &time.year, &time.month, &time.day)) {
            return true;
        }
        return false;
    }
    return false;
}

bool SystemOperation::callDbusCalendar(SchedulingInfo& input, SchedulingType type) const {
    GError* error = nullptr;
    GDBusProxy* proxy = g_dbus_proxy_new_for_bus_sync(
        G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr, "org.ukui.calendar.Notifications",
        "/org/ukui/calendar/Notifications", "org.ukui.calendar.Notifications", nullptr, &error);
    if (error != nullptr) {
        g_printerr("Error creating proxy : %s\n", error->message);
        g_error_free(error);
        return false;
    }
    if (type == SchedulingType::createSchedule) {
        GVariant* result =
            g_dbus_proxy_call_sync(proxy, "ScheduleNotification",
                                   g_variant_new("(iiiiiii&s)", input.startTime.hour, input.startTime.minute,
                                                 input.startTime.day, input.startTime.month, input.startTime.year,
                                                 input.endTime.minute, input.endTime.hour, input.script.c_str()),
                                   G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
        if (error != nullptr) {
            g_printerr("Error calling method: %s\n", error->message);
            g_error_free(error);
            return false;
        }
        g_variant_unref(result);
        g_object_unref(proxy);
    }
    if (type == SchedulingType::openSchedule) {
        GVariant* result = g_dbus_proxy_call_sync(
            proxy, "OpenSchedule",
            g_variant_new("(iii)", input.startTime.day, input.startTime.month, input.startTime.year),
            G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
        if (error != nullptr) {
            g_printerr("Error calling method: %s\n", error->message);
            g_error_free(error);
            return false;
        }
        g_variant_unref(result);
        g_object_unref(proxy);
    }

    return true;
}

bool SystemOperation::callDbusMagnifier() const {
    GError* error = nullptr;
    GDBusProxy* proxy =
        g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, nullptr, "org.ukui.magnifier",
                                      "/Magnifier", "org.ukui.magnifier", nullptr, &error);
    if (error != nullptr) {
        g_printerr("Error creating proxy : %s\n", error->message);
        g_error_free(error);
        return false;
    }

    GVariant* result = g_dbus_proxy_call_sync(proxy, "ZoomIn", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, nullptr, &error);
    if (error != nullptr) {
        g_printerr("Error calling method: %s\n", error->message);
        g_error_free(error);
        return false;
    }
    g_variant_unref(result);
    g_object_unref(proxy);

    return true;
}