import os
import re
import sys
import time
import json
import ctypes
import logging
from datetime import datetime
from urllib.parse import urlparse, urljoin, quote
from colorama import init, Fore, Style

# =================== Setup & Logging ===================
init()

def _env(key, default=None):
    return os.environ.get(key, default)

LOG_LEVEL_NAME = _env("MYTHMU_LOG_LEVEL", "INFO").upper()
LOG_TO_FILE    = _env("MYTHMU_LOG_FILE", "").strip()
LOG_LEVEL      = getattr(logging, LOG_LEVEL_NAME, logging.INFO)

class ClientFormatter(logging.Formatter):
    def format(self, record):
        ts = datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S,%f")[:-3]
        color_map = {
            "DEBUG": Fore.BLUE,
            "INFO": Fore.YELLOW,
            "WARNING": Fore.MAGENTA,
            "ERROR": Fore.RED,
            "CRITICAL": Fore.RED + Style.BRIGHT,
        }
        color = color_map.get(record.levelname, Fore.WHITE)
        return f"{color}{ts} - CLIENT - {record.levelname} - {record.getMessage()}{Style.RESET_ALL}"

log = logging.getLogger("MythMuClient")
log.setLevel(logging.DEBUG)
_stream = logging.StreamHandler()
_stream.setLevel(LOG_LEVEL)
_stream.setFormatter(ClientFormatter())
log.handlers.clear()
log.addHandler(_stream)

if LOG_TO_FILE:
    try:
        _fh = logging.FileHandler(LOG_TO_FILE, encoding="utf-8")
        _fh.setLevel(logging.DEBUG)
        _fh.setFormatter(logging.Formatter("%(asctime)s - CLIENT - %(levelname)s - %(message)s"))
        log.addHandler(_fh)
        log.info(f"File logging enabled -> {LOG_TO_FILE}")
    except Exception as e:
        log.error(f"Could not open log file '{LOG_TO_FILE}': {e}")

log.info(f"Log level: {LOG_LEVEL_NAME}")

ENABLE_DEV_FEATURES = _env("MYTHMU_DEV_TOOLS", "1") not in ("0", "false", "False")

try:
    from cefpython3 import cefpython as cef
except Exception as e:
    log.critical(f"CEF IMPORT ERROR: {e}")
    sys.exit(1)

# =================== API (ONLY login + game/gl) ===================
def _norm_base(s: str) -> str:
    return (s or "").strip().rstrip("/")

API_BASE = _env("MYTHMU_API_BASE", "http://127.0.0.1")
if API_BASE and not API_BASE.startswith(("http://", "https://")):
    API_BASE = "http://" + API_BASE
API_BASE = _norm_base(API_BASE)

API_LOGIN_URL   = urljoin(API_BASE + "/", "api/auth/login")
API_GAME_GL_URL = urljoin(API_BASE + "/", "api/game/gl")
API_ORIGIN      = f"{urlparse(API_BASE).scheme}://{urlparse(API_BASE).netloc}"

log.info(f"API_BASE      = {API_BASE}")
log.info(f"API_LOGIN_URL = {API_LOGIN_URL}")
log.info(f"API_GAME_GL   = {API_GAME_GL_URL}")
log.info(f"API_ORIGIN    = {API_ORIGIN}")

# =================== Paths & helpers ===================
def resource_path(rel: str) -> str:
    try:
        base = sys._MEIPASS
        log.debug(f"resource_path: _MEIPASS={base}")
    except Exception:
        base = os.path.abspath(".")
        log.debug(f"resource_path: cwd={base}")
    if hasattr(sys, "frozen") and not hasattr(sys, "_MEIPASS"):
        base = os.path.join(os.path.abspath(os.path.dirname(sys.executable)), "LauncherData")
        log.debug(f"resource_path: frozen -> LauncherData={base}")
    p = os.path.join(base, rel)
    log.debug(f"resource_path('{rel}') -> {p}")
    return p

def _file_url(path: str) -> str:
    ap = os.path.abspath(path).replace("\\", "/")
    furl = "file:///" + quote(ap)
    log.debug(f"_file_url({path}) -> {furl}")
    return furl

def set_icon(browser):
    try:
        if os.name != "nt":
            log.debug("set_icon: non-Windows, skip")
            return
        base = os.path.abspath(os.path.dirname(sys.executable)) if hasattr(sys, "frozen") else os.path.abspath(".")
        icon = os.path.join(base, "icon.ico")
        if not os.path.exists(icon):
            icon2 = os.path.join(os.path.abspath("."), "icon.ico")
            if os.path.exists(icon2):
                icon = icon2
            else:
                log.debug("set_icon: icon.ico not found, skip")
                return
        hwnd = browser.GetWindowHandle()
        user32 = ctypes.windll.user32
        WM_SETICON = 0x0080
        ICON_SMALL, ICON_BIG = 0, 1
        LR_LOADFROMFILE, IMAGE_ICON = 0x0010, 1
        hbig = user32.LoadImageW(0, icon, IMAGE_ICON, 0, 0, LR_LOADFROMFILE)
        if hbig: user32.SendMessageW(hwnd, WM_SETICON, ICON_BIG, hbig)
        hsm  = user32.LoadImageW(0, icon, IMAGE_ICON, 0, 0, LR_LOADFROMFILE)
        if hsm:  user32.SendMessageW(hwnd, WM_SETICON, ICON_SMALL, hsm)
        log.info(f"Window icon set: {icon}")
    except Exception as e:
        log.warning(f"Icon warn: {e}")

def _base_dir():
    if hasattr(sys, "_MEIPASS"):
        p = sys._MEIPASS
        log.debug(f"_base_dir: _MEIPASS={p}")
        return p
    if not hasattr(sys, "frozen"):
        try:
            cef_dir = os.path.dirname(cef.__file__)
            if os.path.exists(os.path.join(cef_dir, "subprocess.exe")):
                log.debug(f"_base_dir: cef dir={cef_dir}")
                return cef_dir
            else:
                log.debug("_base_dir: no subprocess.exe in cef dir, using cwd")
                return os.path.abspath(".")
        except NameError:
            log.debug("_base_dir: NameError on cef.__file__, using cwd")
            return os.path.abspath(".")
    exec_dir = os.path.join(os.path.abspath(os.path.dirname(sys.executable)), "LauncherData")
    log.debug(f"_base_dir: frozen LauncherData={exec_dir}")
    return exec_dir

def get_flash_dll():
    try:
        p = resource_path(os.path.join("Plugins", "pepflashplayer.dll"))
        if not os.path.exists(p):
            here = os.path.dirname(os.path.realpath(__file__))
            p2 = os.path.join(here, "Plugins", "pepflashplayer.dll")
            if os.path.exists(p2):
                log.info(f"PepperFlash DLL (local): {p2}")
                return p2
            log.error("Missing Plugins\\pepflashplayer.dll")
            return None
        log.info(f"PepperFlash DLL (bundle): {p}")
        return p
    except Exception as e:
        log.error(f"pepflash error: {e}")
        return None

# =================== (rest of your code is unchanged until main) ===================
# ... all your functions (inline login, open_game_from_redirect_url, rewrite, guards, handlers, etc.) ...

# =================== Main ===================
def main():
    cef_inited = False
    try:
        base = _base_dir()
        subproc = os.path.join(base, "subprocess.exe")
        log.info(f"CEF base dir: {base}")
        if not os.path.exists(subproc):
            log.critical(f"Missing CEF subprocess: {subproc}")
            sys.exit(1)

        dll = get_flash_dll()
        if not dll:
            sys.exit(1)

        settings = {
            "resources_dir_path": base,
            "locales_dir_path": os.path.join(base, "locales"),
            "browser_subprocess_path": subproc,
            "log_severity": cef.LOGSEVERITY_DISABLE,
        }
        switches = {
            "ppapi-flash-path": dll,
            "allow-outdated-plugins": True,
            "no-sandbox": "",
            "disable-gpu-vsync": "",
            "disable-features": "VizDisplayCompositor",
            "allow-file-access-from-files": "",
            "allow-file-access": "",
            "disable-gpu": "",
        }

        log.info("CEF settings: " + json.dumps(settings, indent=2))
        log.info("CEF switches: " + json.dumps(switches, indent=2))

        cef.Initialize(settings=settings, switches=switches)
        cef_inited = True
        log.info("CEF initialized")

        browser = cef.CreateBrowserSync(url="about:blank", window_title="MythMu")
        set_icon(browser)

        # JS bridge
        bindings = cef.JavascriptBindings(bindToFrames=False, bindToPopups=False)
        def _open_game_from_redirect_url(final_url=None):
            log.info(f"[JS bridge] openGameFromRedirectUrl called: {final_url}")
            try:
                open_game_from_redirect_url(browser, final_url or "")
            except Exception as e:
                log.error(f"openGameFromRedirectUrl error: {e}")
        bindings.SetFunction("openGameFromRedirectUrl", _open_game_from_redirect_url)
        browser.SetJavascriptBindings(bindings)
        log.info("Javascript bridge registered")

        handler = AppHandler(browser)
        browser.SetClientHandler(handler)

        if ENABLE_DEV_FEATURES:
            try:
                browser.ShowDevTools()
                log.info("DevTools opened (ENABLE_DEV_FEATURES=1)")
            except Exception as e:
                log.warning(f"DevTools warn: {e}")
        else:
            log.info("DevTools disabled (ENABLE_DEV_FEATURES=0)")

        # === Fix: Reset resize cursor when refocusing window ===
        import threading

        def force_cursor_reset(hwnd=None):
            try:
                user32 = ctypes.windll.user32
                if not hwnd:
                    hwnd = browser.GetWindowHandle()
                user32.SetCursor(user32.LoadCursorW(0, 32512))  # IDC_ARROW
                log.debug("Cursor reset to IDC_ARROW")
            except Exception as e:
                log.warning(f"Cursor reset failed: {e}")

        def on_got_focus(browser, **_):
            log.debug("Browser gained focus — resetting cursor")
            threading.Timer(0.05, lambda: force_cursor_reset(browser.GetWindowHandle())).start()

        focus_handler = type("FocusHandler", (), {"OnGotFocus": on_got_focus})()
        browser.SetClientHandler(focus_handler)
        log.info("Focus handler for cursor reset registered")
        # === End fix ===

        log_local_assets_presence()
        load_local_login(browser)

        log.info("Entering CEF message loop")
        cef.MessageLoop()

    except Exception as e:
        log.critical(f"CRITICAL in main: {e}")
    finally:
        try:
            if cef_inited:
                cef.Shutdown()
                log.info("CEF shutdown completed")
        except Exception as e:
            log.error(f"Shutdown error: {e}")

if __name__ == "__main__":
    main()
