#include "Application.hpp"
#include "../core/Config.hpp"
#include "../extractor/GmadExtractor.hpp"
#include "../platform/Dialogs.hpp"
#include "../platform/PathUtils.hpp"
#include "../platform/Process.hpp"

#include <iostream>

namespace GModTeach {
namespace App {

namespace {

std::filesystem::path ResolveAssetPath(const std::string& filename) {
    const auto exeDir = Platform::PathUtils::GetExecutableDirectory();
    
    // execheck
    if (std::filesystem::exists(exeDir / filename)) {
        return exeDir / filename;
    }
    // asset checkk
    if (std::filesystem::exists(exeDir / "assets" / filename)) {
        return exeDir / "assets" / filename;
    }
    // current dir checkk!
    if (std::filesystem::exists(filename)) {
        return std::filesystem::path(filename);
    }
    return exeDir / filename;
}

} 

Application::Application()
    : m_btnOpenWeb({ 20, 20, 200, 40 }, "Open Web"),
      m_btnInstall({ 20, 80, 200, 40 }, "Install All"),
      m_btnInstallClean({ 20, 140, 200, 40 }, "Install + Clean"),
      m_btnResetConfig({ 20, 200, 200, 40 }, "Reset Config"),
      m_progressBar({ 240, 250, 535, 20 }),
      m_titleRect({ 240, 20, 535, 220 }) {}

Application::~Application() {
    Shutdown();
}

bool Application::Initialize() {
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) < 0) {
        std::cerr << "[ERROR] SDL_Init failed: " << SDL_GetError() << "\n";
        return false;
    }

    if (TTF_Init() == -1) {
        std::cerr << "[ERROR] TTF_Init failed: " << TTF_GetError() << "\n";
        return false;
    }

    constexpr int imgFlags = IMG_INIT_PNG;
    if (!(IMG_Init(imgFlags) & imgFlags)) {
        std::cerr << "[ERROR] IMG_Init failed: " << IMG_GetError() << "\n";
        return false;
    }

    // SDL WINDOW!
    m_window = SDL_CreateWindow(
        "Gmod-Teach-Gma-Autoinstaller",
        SDL_WINDOWPOS_CENTERED,
        SDL_WINDOWPOS_CENTERED,
        UI::WINDOW_WIDTH,
        UI::WINDOW_HEIGHT,
        SDL_WINDOW_SHOWN
    );

    if (!m_window) {
        std::cerr << "[ERROR] SDL_CreateWindow failed: " << SDL_GetError() << "\n";
        return false;
    }

    m_renderer = SDL_CreateRenderer(
        m_window,
        -1,
        SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
    );

    if (!m_renderer) {
        std::cerr << "[ERROR] SDL_CreateRenderer failed: " << SDL_GetError() << "\n";
        return false;
    }

    // ttf load
    const auto fontFile = ResolveAssetPath(UI::FONT_PATH);
    m_font = TTF_OpenFont(fontFile.string().c_str(), 18);
    if (!m_font) {
        std::cerr << "[WARN] Failed to load font from: " << fontFile << " (" << TTF_GetError() << ")\n";
    }

    // title (image) load
    const auto titleImg = ResolveAssetPath(UI::TITLE_IMAGE_PATH);
    m_titleTexture = IMG_LoadTexture(m_renderer, titleImg.string().c_str());
    if (!m_titleTexture) {
        std::cerr << "[WARN] Failed to load title texture from: " << titleImg << "\n";
    }

    CheckAndPromptConfiguration();

    m_running = true;
    return true;
}

void Application::CheckAndPromptConfiguration() {
    const auto iniPath = Core::ConfigManager::GetDefaultConfigPath();
    const bool loaded = Core::ConfigManager::Load(iniPath, m_config);

    if (!loaded) {
        auto gmodFolder = Platform::Dialogs::OpenFolderDialog(L"Select your Garry's Mod Root Directory");
        auto gmadLsFolder = Platform::Dialogs::OpenFolderDialog(L"Select your GMAD-LS Workshop/Downloads Directory");

        if (!gmodFolder.has_value() || !gmadLsFolder.has_value() ||
            gmodFolder->empty() || gmadLsFolder->empty()) {
            Platform::Dialogs::ShowErrorDialog("Configuration Incomplete", "Both directories must be selected to proceed.");
            return;
        }

        m_config.gmodDir = *gmodFolder;
        m_config.gmadLsDir = *gmadLsFolder;

        Core::ConfigManager::Save(iniPath, m_config);
    }
}

void Application::Shutdown() {
    if (m_workerThread.joinable()) {
        m_workerThread.join();
    }

    if (m_titleTexture) {
        SDL_DestroyTexture(m_titleTexture);
        m_titleTexture = nullptr;
    }

    if (m_font) {
        TTF_CloseFont(m_font);
        m_font = nullptr;
    }

    if (m_renderer) {
        SDL_DestroyRenderer(m_renderer);
        m_renderer = nullptr;
    }

    if (m_window) {
        SDL_DestroyWindow(m_window);
        m_window = nullptr;
    }

    IMG_Quit();
    TTF_Quit();
    SDL_Quit();
}

void Application::StartPipelineAsync(bool cleanAfterwards) {
    if (m_isProcessing.load()) {
        return;
    }

    if (!m_config.IsValid()) {
        CheckAndPromptConfiguration();
        if (!m_config.IsValid()) {
            return;
        }
    }

    if (m_workerThread.joinable()) {
        m_workerThread.join();
    }

    m_isProcessing.store(true);
    m_progress.store(0.0f);

    {
        std::lock_guard<std::mutex> lock(m_statusMutex);
        m_statusMessage = "Starting extraction...";
    }

    m_workerThread = std::thread([this, cleanAfterwards]() {
        auto progressCb = [this](float prog, const std::string& status) {
            m_progress.store(prog);
            {
                std::lock_guard<std::mutex> lock(m_statusMutex);
                m_statusMessage = status;
            }
        };

        auto stats = Extractor::GmadExtractor::RunInstallationPipeline(
            m_config,
            cleanAfterwards,
            progressCb
        );

        m_progress.store(1.0f);
        {
            std::lock_guard<std::mutex> lock(m_statusMutex);
            m_statusMessage = "Done! Processed " + std::to_string(stats.binsProcessed + stats.gmasProcessed) +
                              " addons (" + std::to_string(stats.errors) + " errors)";
        }

        m_isProcessing.store(false);
    });
}

void Application::HandleActionWeb() {
    Platform::Process::OpenBrowserURL(UI::REPO_URL);
}

void Application::HandleActionReset() {
    const auto iniPath = Core::ConfigManager::GetDefaultConfigPath();
    Core::ConfigManager::Reset(iniPath);

    Platform::Dialogs::ShowInfoDialog("Configuration Reset", "Configuration has been reset. The application will restart.");
    Platform::Process::RestartSelf();
}

void Application::ProcessEvents() {
    SDL_Event e;
    while (SDL_PollEvent(&e)) {
        if (e.type == SDL_QUIT) {
            m_running = false;
        }

        if (e.type == SDL_MOUSEBUTTONDOWN && e.button.button == SDL_BUTTON_LEFT) {
            const int mx = e.button.x;
            const int my = e.button.y;

            if (!m_isProcessing.load()) {
                if (m_btnOpenWeb.Contains(mx, my)) {
                    HandleActionWeb();
                } else if (m_btnInstall.Contains(mx, my)) {
                    StartPipelineAsync(false);
                } else if (m_btnInstallClean.Contains(mx, my)) {
                    StartPipelineAsync(true);
                } else if (m_btnResetConfig.Contains(mx, my)) {
                    HandleActionReset();
                }
            }
        }
    }
}

void Application::Update() {
    int mx = 0, my = 0;
    SDL_GetMouseState(&mx, &my);

    const bool processing = m_isProcessing.load();

    m_btnOpenWeb.SetEnabled(!processing);
    m_btnInstall.SetEnabled(!processing);
    m_btnInstallClean.SetEnabled(!processing);
    m_btnResetConfig.SetEnabled(!processing);

    m_btnOpenWeb.UpdateHover(mx, my);
    m_btnInstall.UpdateHover(mx, my);
    m_btnInstallClean.UpdateHover(mx, my);
    m_btnResetConfig.UpdateHover(mx, my);

    m_progressBar.SetProgress(m_progress.load());
    {
        std::lock_guard<std::mutex> lock(m_statusMutex);
        m_progressBar.SetStatusText(m_statusMessage);
    }
}

void Application::Render() {
    SDL_SetRenderDrawColor(
        m_renderer,
        UI::CLR_GMOD_BLUE.r,
        UI::CLR_GMOD_BLUE.g,
        UI::CLR_GMOD_BLUE.b,
        UI::CLR_GMOD_BLUE.a
    );
    SDL_RenderClear(m_renderer);

    //renders--------------------------------------------------------------
    m_btnOpenWeb.Draw(m_renderer, m_font);
    m_btnInstall.Draw(m_renderer, m_font);
    m_btnInstallClean.Draw(m_renderer, m_font);
    m_btnResetConfig.Draw(m_renderer, m_font);

    // title
    if (m_titleTexture) {
        SDL_RenderCopy(m_renderer, m_titleTexture, nullptr, &m_titleRect);
    }

    // progress
    m_progressBar.Draw(m_renderer, m_font);

    // frame
    SDL_RenderPresent(m_renderer);
}

int Application::Run() {
    if (!Initialize()) {
        return 1;
    }

    while (m_running) {
        ProcessEvents();
        Update();
        Render();
        SDL_Delay(16);
    }

    return 0;
}

} 
} 
