//
// Created by taynpg on 24-8-6.
//

#include "FunctionImp.h"
#include "data.hpp"
#include <filesystem>
#include <fstream>
#include <wx/base64.h>
#include <wx/filename.h>
#include <wx/log.h>
#include <wx/msgdlg.h>
#include <wx/stdpaths.h>
#include <wx/textfile.h>
#include <wx/tokenzr.h>

namespace fs = std::filesystem;

CFunPack::CFunPack() = default;

bool CFunPack::gen(const wxString& bin, const wxString& out_dir, const wxArrayString& dirs)
{
    auto ret_source = get_depend_on(bin, dirs);
    auto result = parse_result(ret_source);
    return handle_copy(result, out_dir.ToStdString());
}

wxArrayString CFunPack::get_depend_on(const wxString& bin, const wxArrayString& dirs)
{
    filename_ = fs::path(bin.ToStdWstring()).filename().wstring();

    wxArrayString array;
    wxString cmd;
    if (!dirs.empty()) {
        cmd.append("export LD_LIBRARY_PATH=$LD_LIBRARY_PATH");
        for (const auto& data : dirs) {
            cmd.append(":" + data);
        }
    }
    cmd.append(" && ldd " + bin);

    FILE* pf = nullptr;
    if ((pf = popen(cmd.c_str(), "r")) == nullptr) {
        return array;
    }

    char buffer[1024]{};
    wxString result{};
    while (std::fgets(buffer, sizeof(buffer), pf)) {
        result.append(buffer);
    }

    array = wxStringTokenize(result, wxT("\t"), wxTOKEN_DEFAULT);
    return array;
}

std::list<wxString> CFunPack::parse_result(const wxArrayString& array)
{
    std::list<wxString> ret;
    wxArrayString simlog;
    wxArrayString bk_arry = array;

    for (auto& data : bk_arry) {
        if (data.empty()) {
            continue;
        }

        if (data.Contains("not found")) {
            simlog.push_back(wxT("未找到依赖:" + data));
            continue;
        }

        data.Replace("=>", "");
        wxArrayString tokens = wxStringTokenize(data, " ");
        wxString dy;
        if (tokens.size() == 4) {
            dy = tokens[2];
        }
        if (tokens.size() == 3) {
            dy = tokens[1];
        }
        if (dy.starts_with("/lib")) {
            continue;
        }
        if (!dy.empty()) {
            ret.push_back(dy);
        }
    }
    return ret;
}

bool CFunPack::handle_copy(const std::list<wxString>& rets, const wxString& dest_dir)
{
    std::vector<fs::path> need_copy;
    for (const auto& item : rets) {
        // fs::path item_path(item);
        // if (fs::is_symlink(item.ToStdString())) {
        //     auto real = fs::read_symlink(item.ToStdWstring());
        //     auto read_full = fs::path(item).parent_path().append(real.wstring());
        //     need_copy.push_back(read_full);
        //     wxMessageBox(wxString(read_full.wstring()));
        // }
        need_copy.push_back(item.ToStdWstring());
    }

    dest_dir_ = fs::path(dest_dir.ToStdWstring()).append(filename_.ToStdWstring()).wstring();
    fs::create_directories(dest_dir_.ToStdWstring());
    for (const auto& item : need_copy) {
        std::wstring new_path =
            fs::path(dest_dir_.ToStdWstring()).append(item.filename().wstring()).wstring();
        fs::copy_file(item, new_path, fs::copy_options::overwrite_existing);
    }
    return true;
}

wxString ReadFileAndEncodeBase64(const wxString& filePath)
{
    // 打开文件
    wxFile file(filePath, wxFile::read);
    if (!file.IsOpened()) {
        wxLogError("Failed to open file: %s", filePath);
        return wxString();
    }

    // 获取文件大小
    wxFileOffset fileSize = file.Length();
    if (fileSize <= 0) {
        wxLogError("File is empty or invalid: %s", filePath);
        return wxString();
    }

    // 读取文件数据到wxMemoryBuffer
    wxMemoryBuffer buffer(fileSize);
    file.Read(buffer.GetData(), fileSize);
    file.Close();

    // 进行Base64编码
    wxString encoded = wxBase64Encode(buffer.GetData(), buffer.GetDataLen());
    return encoded;
}
bool CPublic::gen_default_png(const wxString& path)
{
    wxMemoryBuffer decode = wxBase64Decode(default_png);
    std::ofstream out(path, std::ios::binary);
    if (!out.is_open()) {
        return false;
    }
    const auto& d = decode.GetData();
    auto len = decode.GetDataLen();
    out.write(static_cast<const char*>(decode.GetData()), decode.GetBufSize());
    out.close();
    return true;
}

CFunInstall::CFunInstall() = default;

bool CFunInstall::install(const wxString& file, const wxString& category, const wxString& ico)
{
    fs::path full_path(file.ToStdWstring());
    wxString file_name = full_path.filename().wstring();
    wxString script_name = file_name + "_run.sh";
    wxString script_full_path =
        fs::path(file.ToStdWstring()).parent_path().append(script_name.ToStdWstring()).wstring();

    wxString script_content =
        "IyEvYmluL2Jhc2gKU0NSSVBUX0RJUj0kKGNkICIkKGRpcm5hbWUgIiQwIikiICYmIHB3ZCkKY2QgIiRTQ1JJUFRfRElSIiB8fCBl"
        "eGl0CmV4cG9ydCBMRF9MSUJSQVJZX1BBVEg9JExE"
        "X0xJQlJBUllfUEFUSDoiJFNDUklQVF9ESVIiCiIkU0NSSVBUX0RJUi9yZXBsYWNlX3N0ciIgIiRAIgo=";
    wxMemoryBuffer decodeData = wxBase64Decode(script_content);
    script_content =
        wxString::FromUTF8(static_cast<const char*>(decodeData.GetData()), decodeData.GetBufSize());

    wxString desktop_content = "W0Rlc2t0b3AgRW50cnldCk5hbWU9cmVfbmFtZQpDb21tZW50PXJlX2Rlc2NyaWJlCkV4ZWM9cmVfc"
                               "GF0aApJY29uPXJlX2ljb24KVGVybWluYWw9ZmFsc2UKVHlwZT1BcHBsaWNhdGlv"
                               "bgpDYXRlZ29yaWVzPXJlX2NhdGVnb3J5Owo=";
    wxMemoryBuffer decodeData2 = wxBase64Decode(desktop_content);
    desktop_content =
        wxString::FromUTF8(static_cast<const char*>(decodeData2.GetData()), decodeData2.GetDataLen());

    // write temp file
    fs::path temp(wxGetHomeDir().ToStdWstring());
    temp.append(".config").append("LinuxPack");
    fs::path temp_png = temp;
    temp_png.append("default.png");

    wxString ico_path = ico;
    if (ico.empty() && CPublic::gen_default_png(temp_png.wstring())) {
        ico_path = temp_png.wstring();
    }

    script_content.Replace("replace_str", file_name);
    desktop_content.Replace("re_name", file_name);
    desktop_content.Replace("re_describe", "default");
    desktop_content.Replace("re_path", script_full_path);
    desktop_content.Replace("re_icon", ico_path);
    desktop_content.Replace("re_category", category);

    if (!CPublic::write_txt(script_content, script_full_path)) {
        return false;
    }
    wxString chmod_sh = "chmod +x " + script_full_path;
    wxArrayString output;
    wxExecute(chmod_sh, output, wxEXEC_ASYNC);

    fs::create_directories(temp);
    temp.append((file_name + ".desktop").ToStdWstring());

    if (!CPublic::write_txt(desktop_content, temp.wstring())) {
        return false;
    }

    // purdir
    wxString pur_dir = "/usr/share/applications";
    wxString desktop_cmd = "pkexec cp " + temp.wstring() + " " + pur_dir;
    output.clear();
    wxExecute(desktop_cmd, output, wxEXEC_ASYNC);

    return true;
}

bool CPublic::write_txt(const wxString& content, const wxString& path)
{
    wxTextFile file;

    if (wxFileName::FileExists(path)) {
        // 如果存在,删除文件
        if (!wxRemoveFile(path)) {
            wxLogError(wxT("无法删除现有文件: %s"), path);
            return false;
        }
    }

    if (!file.Create(path)) {
        wxLogError(wxT("无法创建文件: %s"), path);
        return false;
    }

    if (!file.Open(path)) {
        wxLogError(wxT("Open File Failed: %s"), path);
        return false;
    }
    file.AddLine(content);
    file.Write();   // 写入内容
    file.Close();   // 关闭文件

    return true;
}