#include "auto_login.hpp"
#include "common/logger.hpp"
#include "common/window_utils.hpp"

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <algorithm>

AutoLogin::AutoLogin(AutoLoginConfig config)
    : m_config(std::move(config)) {}

void AutoLogin::Reset() {
    m_state = AutoLoginState::IDLE;
    m_stateStartMs = 0;
    m_lastActionMs = 0;
    m_retryCount = 0;
    m_lastDisconnectReason.clear();
}

void AutoLogin::OnDisconnect(const std::string& reason, uint64_t nowMs) {
    if (!m_config.enabled) return;

    if (m_state == AutoLoginState::IDLE) {
        m_lastDisconnectReason = reason;
        m_state = AutoLoginState::DISCONNECTED_WAITING;
        m_stateStartMs = nowMs;
        m_lastActionMs = nowMs;
        m_retryCount = 0;

        const std::string msg = "[AutoLogin] >> PHÁT HIỆN MẤT KẾT NỐI (" + reason
            + ")! Chờ " + std::to_string(m_config.reconnectDelayMs)
            + "ms cooldown trước khi tự động kết nối lại... <<";
        std::cout << "\n" << msg << std::endl;
        CoreLog(msg);
    }
}

void AutoLogin::TriggerManualLogin(uint64_t nowMs) {
    m_lastDisconnectReason = "Manual Trigger";
    m_state = AutoLoginState::DISMISS_DIALOG;
    m_stateStartMs = nowMs;
    m_lastActionMs = nowMs;
    m_retryCount = 0;

    const std::string msg = "[AutoLogin] >> KÍCH HOẠT THỬ ĐĂNG NHẬP THỦ CÔNG TỪ GIAO DIỆN <<";
    std::cout << "\n" << msg << std::endl;
    CoreLog(msg);
}

void AutoLogin::OnLoadingFinished(const std::string& areaName, uint64_t /*nowMs*/) {
    if (IsActive()) {
        const std::string msg = "[AutoLogin] >> ĐĂNG NHẬP THÀNH CÔNG! Đã vào lại khu vực: "
            + areaName + ". Sẵn sàng tiếp tục vận hành bot. <<";
        std::cout << "\n" << msg << std::endl;
        CoreLog(msg);
        Reset();
    }
}

void AutoLogin::FocusGameWindow() {
    common::EnsurePoe2WindowFocus(200);
}

bool AutoLogin::GetGameWindowRect(int& outLeft, int& outTop, int& outWidth, int& outHeight) {
    HWND hwnd = common::FindPoe2Window();
    if (hwnd) {
        RECT rc{};
        if (GetWindowRect(hwnd, &rc)) {
            outLeft = rc.left;
            outTop = rc.top;
            outWidth = rc.right - rc.left;
            outHeight = rc.bottom - rc.top;
            if (outWidth > 100 && outHeight > 100) {
                return true;
            }
        }
    }

    // Fallback: Lấy kích thước toàn màn hình chính
    outLeft = 0;
    outTop = 0;
    outWidth = GetSystemMetrics(SM_CXSCREEN);
    outHeight = GetSystemMetrics(SM_CYSCREEN);
    if (outWidth <= 0) outWidth = 2560;
    if (outHeight <= 0) outHeight = 1440;
    return false;
}

void AutoLogin::SendClick(int x, int y, KMBoxNet& kmbox) {
    kmbox.MoveMouseSmooth(x, y, m_curveGen, 15, 1);
    kmbox.ClickMouseHumanized(1, m_curveGen, 50.0f, 10.0f);
}

void AutoLogin::SendKey(uint8_t vk, KMBoxNet& kmbox, uint32_t holdMs) {
    if (kmbox.IsEmulationMode()) {
        keybd_event(vk, 0, 0, 0);
        std::this_thread::sleep_for(std::chrono::milliseconds(holdMs));
        keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
    } else {
        // Ánh xạ một số phím cơ bản sang HID code cho phần cứng KMBox
        uint8_t hid = 0;
        if (vk == VK_RETURN) hid = 0x28;
        else if (vk == VK_SPACE) hid = 0x2C;
        else if (vk == VK_ESCAPE) hid = 0x29;
        else if (vk == VK_BACK) hid = 0x2A;
        else if (vk == VK_TAB) hid = 0x2B;
        else hid = vk;

        kmbox.KeyPress(hid, holdMs);
    }
}

void AutoLogin::SendText(const std::string& text, KMBoxNet& kmbox) {
    for (char c : text) {
        if (kmbox.IsEmulationMode()) {
            SHORT vkCode = VkKeyScanA(c);
            if (vkCode != -1) {
                uint8_t vk = static_cast<uint8_t>(vkCode & 0xFF);
                bool shift = (vkCode & 0x100) != 0;

                if (shift) keybd_event(VK_SHIFT, 0, 0, 0);
                keybd_event(vk, 0, 0, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(20));
                keybd_event(vk, 0, KEYEVENTF_KEYUP, 0);
                if (shift) keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
            }
        } else {
            // Emulation fallback cho text nếu đang dùng KMBox
            SHORT vkCode = VkKeyScanA(c);
            if (vkCode != -1) {
                uint8_t vk = static_cast<uint8_t>(vkCode & 0xFF);
                kmbox.KeyPress(vk, 25);
            }
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(25));
    }
}

bool AutoLogin::Tick(KMBoxNet& kmbox, uint64_t nowMs) {
    if (m_state == AutoLoginState::IDLE) return false;

    switch (m_state) {
    case AutoLoginState::DISCONNECTED_WAITING: {
        // Chờ cooldown giải phóng socket
        if (nowMs - m_stateStartMs >= m_config.reconnectDelayMs) {
            m_state = AutoLoginState::DISMISS_DIALOG;
            m_stateStartMs = nowMs;
            m_lastActionMs = nowMs;
            return true;
        }
        break;
    }

    case AutoLoginState::DISMISS_DIALOG: {
        FocusGameWindow();
        int left = 0, top = 0, width = 0, height = 0;
        GetGameWindowRect(left, top, width, height);

        // Hộp thoại lỗi POE2: Nút OK ở giữa màn hình (X = 50%, Y = 56%)
        int okX = left + static_cast<int>(width * 0.50f);
        int okY = top + static_cast<int>(height * 0.56f);

        SendClick(okX, okY, kmbox);
        SendKey(VK_RETURN, kmbox, 30);
        SendKey(VK_SPACE, kmbox, 30);

        const std::string msg = "[AutoLogin] >> Đã bấm OK/Enter/Space đóng hộp thoại thông báo ngắt kết nối tại ("
            + std::to_string(okX) + ", " + std::to_string(okY) + ") <<";
        std::cout << msg << std::endl;
        CoreLog(msg);

        m_state = AutoLoginState::WAIT_LOGIN_SCREEN;
        m_stateStartMs = nowMs;
        m_lastActionMs = nowMs;
        return true;
    }

    case AutoLoginState::WAIT_LOGIN_SCREEN: {
        // Chờ 1.5 giây để màn hình đăng nhập (Login Screen) hiển thị ổn định
        if (nowMs - m_stateStartMs >= 1500) {
            m_state = AutoLoginState::SUBMIT_LOGIN;
            m_stateStartMs = nowMs;
            m_lastActionMs = nowMs;
            return true;
        }
        break;
    }

    case AutoLoginState::SUBMIT_LOGIN: {
        FocusGameWindow();
        int left = 0, top = 0, width = 0, height = 0;
        GetGameWindowRect(left, top, width, height);

        if (!m_config.password.empty()) {
            // Standalone Client: Click ô Password tại (50%, 65%)
            int passX = left + static_cast<int>(width * 0.50f);
            int passY = top + static_cast<int>(height * 0.65f);
            SendClick(passX, passY, kmbox);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));

            // Xóa ký tự cũ: Ctrl + A rồi Backspace
            if (kmbox.IsEmulationMode()) {
                keybd_event(VK_CONTROL, 0, 0, 0);
                keybd_event('A', 0, 0, 0);
                std::this_thread::sleep_for(std::chrono::milliseconds(20));
                keybd_event('A', 0, KEYEVENTF_KEYUP, 0);
                keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
            }
            SendKey(VK_BACK, kmbox, 30);
            std::this_thread::sleep_for(std::chrono::milliseconds(50));

            SendText(m_config.password, kmbox);
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            SendKey(VK_RETURN, kmbox, 50);

            const std::string msg = "[AutoLogin] >> Đã nhập mật khẩu và gửi lệnh Đăng Nhập (Standalone) <<";
            std::cout << msg << std::endl;
            CoreLog(msg);
        } else {
            // Steam / Epic / Saved Password: Click "Log In" tại (50%, 70%) và bấm Enter
            int loginX = left + static_cast<int>(width * 0.50f);
            int loginY = top + static_cast<int>(height * 0.70f);
            SendClick(loginX, loginY, kmbox);
            SendKey(VK_RETURN, kmbox, 50);

            const std::string msg = "[AutoLogin] >> Đã bấm nút 'Log In' / Enter (Steam / Saved Credentials) tại ("
                + std::to_string(loginX) + ", " + std::to_string(loginY) + ") <<";
            std::cout << msg << std::endl;
            CoreLog(msg);
        }

        m_state = AutoLoginState::WAIT_CHAR_SELECT;
        m_stateStartMs = nowMs;
        m_lastActionMs = nowMs;
        return true;
    }

    case AutoLoginState::WAIT_CHAR_SELECT: {
        // Chờ kết nối máy chủ xác thực và tải màn hình chọn nhân vật (~3 giây)
        if (nowMs - m_stateStartMs >= 3000) {
            m_state = AutoLoginState::SELECT_CHARACTER;
            m_stateStartMs = nowMs;
            m_lastActionMs = nowMs;
            return true;
        }

        // Timeout 15 giây nếu không phản hồi -> thử lại
        if (nowMs - m_stateStartMs >= 15000) {
            ++m_retryCount;
            if (m_retryCount >= m_config.maxRetries) {
                const std::string msg = "[AutoLogin] >> Đã thử kết nối lại tối đa "
                    + std::to_string(m_config.maxRetries) + " lần thất bại. Tạm dừng quy trình. <<";
                std::cout << msg << std::endl;
                CoreLog(msg);
                Reset();
            } else {
                const std::string msg = "[AutoLogin] >> Hết thời gian chờ chọn nhân vật. Thử lại từ bước đóng dialog (Lần "
                    + std::to_string(m_retryCount) + ")... <<";
                std::cout << msg << std::endl;
                CoreLog(msg);
                m_state = AutoLoginState::DISMISS_DIALOG;
                m_stateStartMs = nowMs;
                m_lastActionMs = nowMs;
            }
            return true;
        }
        break;
    }

    case AutoLoginState::SELECT_CHARACTER: {
        FocusGameWindow();
        int left = 0, top = 0, width = 0, height = 0;
        GetGameWindowRect(left, top, width, height);

        // Lựa chọn nhân vật trong danh sách bên trái
        if (m_config.charSlot > 1) {
            int slotX = left + static_cast<int>(width * 0.18f);
            int slotY = top + static_cast<int>(height * (0.22f + 0.065f * (m_config.charSlot - 1)));
            SendClick(slotX, slotY, kmbox);
            std::this_thread::sleep_for(std::chrono::milliseconds(250));
        } else {
            // Slot 1: Nhân vật cấp cao nhất trong League mới nhất (Forbidden Rites)
            int slotX = left + static_cast<int>(width * 0.18f);
            int slotY = top + static_cast<int>(height * 0.22f);
            SendClick(slotX, slotY, kmbox);
            std::this_thread::sleep_for(std::chrono::milliseconds(150));
        }

        // Bấm nút "PLAY" ở giữa đáy màn hình (X = 50%, Y = 90%) hoặc phím Enter
        int playX = left + static_cast<int>(width * 0.50f);
        int playY = top + static_cast<int>(height * 0.90f);
        SendClick(playX, playY, kmbox);
        SendKey(VK_RETURN, kmbox, 50);

        const std::string msg = "[AutoLogin] >> Đã chọn nhân vật Slot #"
            + std::to_string(m_config.charSlot) + " (League: " + m_config.leagueName
            + ") tại (" + std::to_string(playX)
            + ", " + std::to_string(playY) + "). Đang chờ màn hình tải hoàn tất... <<";
        std::cout << msg << std::endl;
        CoreLog(msg);

        m_state = AutoLoginState::WAIT_GAME_LOAD;
        m_stateStartMs = nowMs;
        m_lastActionMs = nowMs;
        return true;
    }

    case AutoLoginState::WAIT_GAME_LOAD: {
        // Chờ sự kiện LoadingFinished (thường 5 - 25s)
        // Nếu quá 35s không vào được -> retry
        if (nowMs - m_stateStartMs >= 35000) {
            ++m_retryCount;
            if (m_retryCount >= m_config.maxRetries) {
                const std::string msg = "[AutoLogin] >> Hết thời gian chờ tải màn hình ("
                    + std::to_string(m_retryCount) + "/" + std::to_string(m_config.maxRetries)
                    + "). Ngừng quy trình tự động đăng nhập. <<";
                std::cout << msg << std::endl;
                CoreLog(msg);
                Reset();
            } else {
                const std::string msg = "[AutoLogin] >> Quá 35s chưa vào được game. Thử lại quy trình đăng nhập (Lần "
                    + std::to_string(m_retryCount) + ")... <<";
                std::cout << msg << std::endl;
                CoreLog(msg);
                m_state = AutoLoginState::DISMISS_DIALOG;
                m_stateStartMs = nowMs;
                m_lastActionMs = nowMs;
            }
            return true;
        }
        break;
    }

    default:
        break;
    }

    return false;
}
