#include "raylib.h"
#include "../Menu.h"
#include "../../Initialization/Initialization.h"
#include "../../Backend/Font/FontManager.h"
#include "../../Backend/Network/NetworkClient.h"
#include "../../Backend/HWID/HWIDCollector.h"
#include "../../Backend/TokenStore/TokenStore.h"
#include "../../Backend/Hash/Hash.h"
#include <thread>
#include <cstdio>

menu::Login* menu::Login::Get() {
    static Login instance;
    return &instance;
}

// ── Centralized login validation (cloud-based) ───────
void menu::Login::TryLogin() {
    // Cooldown check (local)
    if (m_blocked) {
        double elapsed = RLGetTime() - m_lastFailedTime;
        if (elapsed >= 3.0) {
            m_blocked = false;
            m_loginAttempts = 0;
            m_errorMessage.clear();
        } else {
            int remaining = (int)(3.0 - elapsed) + 1;
            m_errorMessage = "Too many attempts. Wait " + std::to_string(remaining) + "s...";
            return;
        }
    }

    // Empty field checks
    if (UserName.text.empty() && PassWord.text.empty()) {
        m_errorMessage = "Please enter username and password";
        return;
    }
    if (UserName.text.empty()) {
        m_errorMessage = "Please enter username";
        return;
    }
    if (PassWord.text.empty()) {
        m_errorMessage = "Please enter password";
        return;
    }

    // Prevent double-submit
    if (m_loginPending) return;

    m_loginPending = true;
    m_loginStartTime = RLGetTime();
    m_errorMessage.clear();
    m_serverError.clear();

    // Capture values for thread safety
    std::string username = UserName.text;
    std::string password = PassWord.text;

    // Spawn network thread
    std::thread([this, username, password]() {
        printf("[Login] Attempting login for user: %s\n", username.c_str());

        // Hash password client-side — raw password never leaves this machine
        std::string passwordHash = Hash::ComputeSHA256String(password);

        // Collect HWID components
        const auto& hwid = HWIDCollector::Get()->Collect();

        // Send login request (SHA-256 hash over HTTP)
        auto result = NetworkClient::Get()->Login(
            username, passwordHash,
            hwid.cpu, hwid.board, hwid.disk, hwid.mac);

        std::lock_guard<std::mutex> lock(m_mutex);

        if (result.success) {
            printf("[Login] Success: %s (match_rate=%.2f)\n", result.username.c_str(), result.matchRate);
            m_token = result.accessToken;
            m_refreshToken = result.refreshToken;

            // Persist refresh token for auto-login
            TokenStore::Get()->Save(m_refreshToken, result.username);

            m_loggedIn = true;
            m_loginAttempts = 0;
            m_blocked = false;
        } else {
            printf("[Login] Failed: %s - %s\n", result.errorCode.c_str(), result.errorMessage.c_str());

            if (result.errorCode == "HWID_THRESHOLD") {
                m_serverError = "Hardware not recognized. Contact support.";
            } else if (result.errorCode == "ACCOUNT_EXPIRED") {
                m_serverError = "Account has expired";
            } else if (result.errorCode == "ACCOUNT_DISABLED") {
                m_serverError = "Account is disabled";
            } else if (result.errorCode == "RATE_LIMITED") {
                m_serverError = "Too many attempts. Wait " + std::to_string(result.retryAfter) + "s...";
            } else if (result.errorCode == "NETWORK_ERROR") {
                m_serverError = "Cannot reach server. Retry?";
            } else {
                m_serverError = "Invalid username or password";
                m_loginAttempts++;
                if (m_loginAttempts >= 5) {
                    m_blocked = true;
                    m_lastFailedTime = RLGetTime();
                    m_serverError = "Too many attempts. Wait 3s...";
                }
            }
            m_serverErrorCode = result.errorCode;
        }
        m_loginPending = false;
    }).detach();
}

// ── Reset form to initial state ───────────────────────
void menu::Login::ResetLoginForm() {
    UserName.text.clear();
    PassWord.text.clear();
    UserName.cursor = UserName.selectStart = UserName.selectEnd = 0;
    PassWord.cursor = PassWord.selectStart = PassWord.selectEnd = 0;
    UserName.active = false;
    PassWord.active = false;
    m_errorMessage.clear();
    m_serverError.clear();
    m_loginAttempts = 0;
    m_blocked = false;
    m_prevUserText.clear();
    m_prevPassText.clear();
}

void menu::Login::DrawLoginMenu() {
    auto* ui = Framework::Get();
    auto* font = Font::Get();
    RLVector2 Win_size = Initialization::Get()->Get_WinSize();

    // ── Cooldown tick with countdown ──────────────
    if (m_blocked) {
        double elapsed = RLGetTime() - m_lastFailedTime;
        if (elapsed >= 3.0) {
            m_blocked = false;
            m_loginAttempts = 0;
            m_errorMessage.clear();
        } else {
            int remaining = (int)(3.0 - elapsed) + 1;
            m_errorMessage = "Too many attempts. Wait " + std::to_string(remaining) + "s...";
        }
    }

    // ── Poll async login result ────────────────────
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (!m_loginPending && !m_serverError.empty()) {
            // Thread finished with error — transfer to UI error
            m_errorMessage = m_serverError;
            m_serverError.clear();
            m_errorSetTime = RLGetTime();
            m_prevErrorMessage = m_errorMessage;
        }
    }

    // ── Clear error when user edits text ──────────
    if (UserName.text != m_prevUserText || PassWord.text != m_prevPassText) {
        if (!m_blocked && !m_loginPending)
            m_errorMessage.clear();
    }
    m_prevUserText = UserName.text;
    m_prevPassText = PassWord.text;

    // ── Tab key to switch focus ───────────────────
    if (RLIsKeyPressed(RL_E_KEY_TAB)) {
        if (UserName.active) {
            UserName.active = false;
            PassWord.active = true;
        } else if (PassWord.active) {
            PassWord.active = false;
            UserName.active = true;
        } else {
            UserName.active = true;
        }
    }

    // ── Login form ────────────────────────────────
    float pos_y = 95;
    ui->Label("Name", { (Win_size.x - TextBox_UserName.x) / 2 + 5, pos_y }, 16, 2); pos_y += 15;
    ui->Textbox("##UserName", { (Win_size.x - TextBox_UserName.x) / 2, pos_y, TextBox_UserName.x, 28 }, &UserName); pos_y += 50;
    ui->Label("Password", { (Win_size.x - TextBox_PassWord.x) / 2 + 5, pos_y }, 16, 2); pos_y += 15;
    ui->Textbox("##Password", { (Win_size.x - TextBox_PassWord.x) / 2, pos_y, TextBox_PassWord.x, 28 }, &PassWord); pos_y += 52;

    // Collect login triggers
    bool loginClicked = false;
    bool enterPressed = RLIsKeyPressed(RL_E_KEY_ENTER) && (UserName.active || PassWord.active);

    if (m_loginPending) {
        // ── "Connecting..." animation while pending ──
        double elapsed = RLGetTime() - m_loginStartTime;
        float alpha = (float)(25 + (sin(elapsed * 2.5) + 1.0) * 0.5 * 180.0);

        auto connSize = font->MeasureText("Connecting...", 15);
        font->DrawTextEX("Connecting...",
            { (Win_size.x - connSize.x) / 2, pos_y + 8 },
            15, { 255, 255, 255, (unsigned char)alpha }, 2);

        // Disabled button
        ui->Button("Login", { (Win_size.x - 180) / 2, pos_y, 180, 35 }, true);
    } else {
        loginClicked = ui->Button("Login", { (Win_size.x - 180) / 2, pos_y, 180, 35 });
    }
    pos_y += 60;

    if (loginClicked || enterPressed)
        TryLogin();

    if (ui->Button("Exit", { (Win_size.x - 180) / 2, pos_y, 180, 35 }))
        Initialization::Get()->RequestClose();

    // ── Error message fade tracking ─────────────
    if (m_errorMessage != m_prevErrorMessage) {
        m_errorSetTime = RLGetTime();
        m_prevErrorMessage = m_errorMessage;
    }

    // ── Error message with fade-out ─────────────
    if (!m_errorMessage.empty()) {
        double elapsed = RLGetTime() - m_errorSetTime;
        unsigned char alpha = 255;

        if (elapsed > 1.5) {
            double fadeElapsed = elapsed - 1.5;
            if (fadeElapsed >= 0.5) {
                m_errorMessage.clear();
            } else {
                alpha = (unsigned char)(255.0 * (1.0 - fadeElapsed / 0.5));
            }
        }

        if (!m_errorMessage.empty()) {
            auto errSize = font->MeasureText(m_errorMessage.c_str(), 13);
            font->DrawTextEX(m_errorMessage.c_str(),
                { (Win_size.x - errSize.x) / 2, pos_y + 45 },
                13, { 255, 80, 80, alpha }, 2);
        }
    }
}
