#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <curl/curl.h>
#include <cJSON.h>
#include <openssl/evp.h>

#include "go_deploy.h"
#include "common.h"           // 包含日志 my_zlog 等
#include "device_fileopen.h"  // 包含设备文件读取函数

#define GO_VIDEO_FILENAME "go_video"
#define HASH_TYPE_SHA256 1

char g_webrtc_mode[32] = "web";

// 内存结构体，用于 curl 回调
struct MemoryStruct {
    char *memory;
    size_t size;
};

// CURL 写回调(内存)
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) {
    size_t realsize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)userp;

    char *ptr = realloc(mem->memory, mem->size + realsize + 1);
    if (!ptr) {
        my_zlog_error("not enough memory (realloc returned NULL)");
        return 0;
    }

    mem->memory = ptr;
    memcpy(&(mem->memory[mem->size]), contents, realsize);
    mem->size += realsize;
    mem->memory[mem->size] = 0;

    return realsize;
}

// CURL 写回调(文件)
static size_t WriteFileCallback(void *ptr, size_t size, size_t nmemb, FILE *stream) {
    size_t written = fwrite(ptr, size, nmemb, stream);
    return written;
}

// 计算文件的 MD5 (128位)
static int calculate_file_hash(const char *path, char *output_hash) {
    FILE *file = fopen(path, "rb");
    if (!file) return -1;

    EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
    const EVP_MD *md = EVP_md5(); // 使用 MD5 (128位)
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int md_len;
    char buffer[4096];
    size_t bytes;

    if (!mdctx) {
        fclose(file);
        return -1;
    }

    EVP_DigestInit_ex(mdctx, md, NULL);
    while ((bytes = fread(buffer, 1, sizeof(buffer), file)) != 0) {
        EVP_DigestUpdate(mdctx, buffer, bytes);
    }
    EVP_DigestFinal_ex(mdctx, hash, &md_len);
    EVP_MD_CTX_free(mdctx);
    fclose(file);

    // 转为 hex 字符串
    for (unsigned int i = 0; i < md_len; i++) {
        sprintf(&output_hash[i * 2], "%02x", hash[i]);
    }
    output_hash[md_len * 2] = '\0';
    return 0;
}

// 下载文件
static int download_file(const char *url, const char *filepath) {
    CURL *curl;
    FILE *fp;
    CURLcode res;

    curl = curl_easy_init();
    if (!curl) return -1;

    fp = fopen(filepath, "wb");
    if (!fp) {
        my_zlog_error("Failed to open file for writing: %s", filepath);
        curl_easy_cleanup(curl);
        return -1;
    }

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteFileCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略 SSL 验证(视情况而定)
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

    res = curl_easy_perform(curl);
    
    fclose(fp);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK) {
        my_zlog_error("Download failed: %s", curl_easy_strerror(res));
        remove(filepath); // 下载失败删除残缺文件
        return -1;
    }

    return 0;
}

// 检查是否有 pip3，没有则安装
static int check_and_install_pip3(void) {
    if (system("which pip3 > /dev/null 2>&1") != 0) {
        my_zlog_info("pip3 not found. Installing python3-pip...");
        int install_ret = system("sudo apt-get update && sudo apt-get install -y python3-pip");
        if (install_ret != 0) {
            my_zlog_error("Failed to install python3-pip, return code: %d", install_ret);
            return -1;
        } else {
            my_zlog_info("python3-pip installation completed successfully.");
        }
    }
    return 0;
}

int go_deploy_check_and_update(void) {
    CURL *curl;
    CURLcode res;
    struct MemoryStruct chunk;
    chunk.memory = malloc(1);
    chunk.size = 0;

    int ret = -1;
    char *home_dir = "/home/orangepi";

    char target_dir[256];
    snprintf(target_dir, sizeof(target_dir), "%s/go_video", home_dir);
    my_zlog_info("target_dir: %s", target_dir);
    
    // 确保目录存在
    struct stat st = {0};
    if (stat(target_dir, &st) == -1) {
        if (mkdir(target_dir, 0777) != 0) {
            my_zlog_error("Failed to create directory: %s", target_dir);
        } else {
            my_zlog_info("Created directory: %s", target_dir);
        }
    }
    // 确保目录权限正确
    chmod(target_dir, 0777);

    char target_file_path[512];
    snprintf(target_file_path, sizeof(target_file_path), "%s/%s", target_dir, GO_VIDEO_FILENAME);
    my_zlog_info("target_file_path: %s", target_file_path);

    // 1. 请求 Config
    curl_global_init(CURL_GLOBAL_ALL);
    curl = curl_easy_init();
    if (!curl) {
        my_zlog_error("Failed to init curl");
        free(chunk.memory);
        return -1;
    }

    char url[512];
    const char* device_no = device_id_function(); 
    if (device_no == NULL || strlen(device_no) == 0) {
        my_zlog_error("deviceNo is empty");
        goto cleanup;
    }
    snprintf(url, sizeof(url), "https://fcrs-api.yd-ss.com/device/getConfig?deviceNo=%s", device_no);
    //snprintf(url, sizeof(url), "http://192.168.0.100:8111/device/getConfig?deviceNo=%s", device_no);
    my_zlog_info("Requesting URL: %s", url);

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); 
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);

    res = curl_easy_perform(curl);
    my_zlog_info("curl_easy_perform result: %d", res);

    if (res != CURLE_OK) {
        my_zlog_error("curl_easy_perform() failed: %s", curl_easy_strerror(res));
        goto cleanup;
    }

    // 2. 解析 JSON
    cJSON *root = cJSON_Parse(chunk.memory);
    if (!root) {
        my_zlog_error("JSON Parse Error");
        goto cleanup;
    }

    cJSON *data = cJSON_GetObjectItem(root, "data");
    char *data_str = cJSON_Print(data);
    if (data_str) {
        my_zlog_info("Got config data: %s", data_str);
        free(data_str);
    }
    if (!data) {
        cJSON_Delete(root);
        goto cleanup;
    }

    cJSON *moreInfo = cJSON_GetObjectItem(data, "moreInfo");
    if (!moreInfo) {
        my_zlog_warn("moreInfo is null");
        cJSON_Delete(root);
        goto cleanup;
    }

    cJSON *webrtcMode = cJSON_GetObjectItem(moreInfo, "webrtcMode");
    if (!webrtcMode || !cJSON_IsString(webrtcMode)) {
        cJSON_Delete(root);
        goto cleanup;
    }

    // 更新全局模式变量
    strncpy(g_webrtc_mode, webrtcMode->valuestring, sizeof(g_webrtc_mode) - 1);
    g_webrtc_mode[sizeof(g_webrtc_mode) - 1] = '\0';

    // 3. 检查 webrtcMode
    if (strcmp(webrtcMode->valuestring, "go") == 0) {
        // 检查 pip3 是否安装
        check_and_install_pip3();
        cJSON *goUrl = cJSON_GetObjectItem(moreInfo, "goUrl");
        cJSON *goHash = cJSON_GetObjectItem(moreInfo, "goHash");

        if (!goUrl || !cJSON_IsString(goUrl)) {
             my_zlog_error("goUrl is missing");
             cJSON_Delete(root);
             goto cleanup;
        }

        int need_download = 0;
        
        // 4. 检查文件是否存在
        if (access(target_file_path, F_OK) != 0) {
            my_zlog_info("go_video file not found, downloading...");
            need_download = 1;
        } else {
            // 5. 检查 Hash
            if (goHash && cJSON_IsString(goHash)) {
                char local_hash[65] = {0};
                if (calculate_file_hash(target_file_path, local_hash) == 0) {
                    // 若服务器 hash 也是小写, 直接对比; 否则建议 strcasecmp
                    if (strcasecmp(local_hash, goHash->valuestring) != 0) {
                        my_zlog_info("Hash mismatch. Local: %s, Cloud: %s. Re-downloading...", local_hash, goHash->valuestring);
                        need_download = 1;
                    } else {
                        my_zlog_info("File hash verified. No update needed.");
                    }
                } else {
                    my_zlog_error("Failed to calculate local hash");
                    need_download = 1; 
                }
            } else {
                 my_zlog_warn("goHash is missing, skipping hash check");
                 // 可选择: 强制下载 或 不下载
                 // 这里通常如果不给 hash，如果文件存在假设是好的，或者强制覆盖?
                 // 但需求是 "不一样就下载"，没说没 hash 怎么办。假设存在就不下?
                 // 还是安全起见没 hash 就不校验了
            }
        }

        if (need_download) {
            my_zlog_info("Downloading from: %s", goUrl->valuestring);
            if (download_file(goUrl->valuestring, target_file_path) == 0) {
                 my_zlog_info("Download success.");
                 // 下载后重新给权限
                 chmod(target_file_path, 0777);
            } else {
                 my_zlog_error("Download failed.");
            }
        } else {
             // 即使没下载，确认文件存在后也确保权限正确
             if (access(target_file_path, F_OK) == 0) {
                 chmod(target_file_path, 0777); 
             }
        }

        // 处理 video_proxy.7z
        cJSON *pythonUrl = cJSON_GetObjectItem(moreInfo, "pythonUrl");
        if (pythonUrl && cJSON_IsString(pythonUrl) && strlen(pythonUrl->valuestring) > 0) {
            char target_7z_path[512];
            snprintf(target_7z_path, sizeof(target_7z_path), "%s/video_proxy.7z", target_dir);
            
            int need_download_7z = 0;

            // 检查文件是否存在
            my_zlog_info("target_7z_path：%s",target_7z_path);
            if (access(target_7z_path, F_OK) != 0) {
                my_zlog_info("video_proxy.7z not found, need download.");
                need_download_7z = 1;
            } else {
                // 检查 Hash (使用 pythonHash)
                cJSON *pythonHash = cJSON_GetObjectItem(moreInfo, "pythonHash");
                if (pythonHash && cJSON_IsString(pythonHash)) {
                    char local_hash[65] = {0};
                    if (calculate_file_hash(target_7z_path, local_hash) == 0) {
                        if (strcasecmp(local_hash, pythonHash->valuestring) != 0) {
                            my_zlog_info("7z Hash mismatch. Local: %s, Cloud: %s. Re-downloading...", local_hash, goHash->valuestring);
                            need_download_7z = 1;
                        } else {
                            my_zlog_info("7z Hash verified.");
                        }
                    } else {
                        my_zlog_error("Failed to calculate 7z local hash");
                        need_download_7z = 1;
                    }
                } else {
                    my_zlog_warn("goHash missing for 7z check");
                }
            }

            if (need_download_7z) {
                my_zlog_info("Downloading 7z from: %s", pythonUrl->valuestring);
                if (download_file(pythonUrl->valuestring, target_7z_path) == 0) {
                    my_zlog_info("Download 7z success. Preparing to extract...");

                    // 检查 7z 是否安装
                    if (system("which 7z > /dev/null 2>&1") != 0) {
                        my_zlog_info("7z not found. Installing p7zip-full...");
                        // 尝试安装 p7zip-full (Ubuntu)
                        int install_ret = system("sudo apt-get update && sudo apt-get install -y p7zip-full");
                        if (install_ret != 0) {
                            my_zlog_error("Failed to install p7zip-full, return code: %d", install_ret);
                        } else {
                            my_zlog_info("p7zip-full installation completed successfully.");
                        }
                    }



                    // 解压
                    char cmd[1024];
                    // 使用密码 fcrs2025606 解压, -y 自动覆盖, -o 指定输出目录
                    snprintf(cmd, sizeof(cmd), "7z x \"%s\" -o\"%s\" -pfcrs2025606 -y", target_7z_path, target_dir);
                    my_zlog_info("Executing: %s", cmd);

                    int extract_ret = system(cmd);
                    if (extract_ret == 0) {
                        my_zlog_info("Extraction success.");
                    } else {
                        my_zlog_error("Extraction failed with code: %d", extract_ret);
                    }
                } else {
                    my_zlog_error("Download 7z failed.");
                }
            }
        }     
    }

    cJSON_Delete(root);
    ret = 0;

cleanup:
    curl_easy_cleanup(curl);
    free(chunk.memory);
    curl_global_cleanup();

    return ret;
}
