"""
ControlHost — AAhead Control embedded as the in-process Settings section.

The shell is one seamless app (our manifesto): no relaunch, no second process.
AAhead Control lives as a self-contained sub-package (gui.control.*) with its
own design/shared/widgets/backend. Its screens expect an `app` exposing
push/pop/toast/request_exit; this host hands them a small facade backed by its
own screen stack, while toasts and "exit" fold back into the real game shell.

The shell's main loop drives this like any screen: on_action / update / draw.
"""

from __future__ import annotations

import os

import pygame

from ..control.design import tokens as ct
from ..control.shared import render as crender


class _ControlApp:
    """The `app` the embedded Control screens see. Its own stack; toast/exit
    delegate to the real game-shell app."""

    embedded = True   # Control runs as the shell's Settings section → "B back"

    def __init__(self, shell):
        self._shell = shell
        self.stack: list = []

    # Delegations to the real shell — Control screens that need access to
    # the catalog / library / play-state read through the proxy. Catalog
    # and library are the same instances both halves of the app share —
    # mutations made here (Display LCD/TV toggle, cheat enables) are seen
    # by the game shell on resume without any cross-process sync.

    @property
    def catalog(self):
        return self._shell.catalog

    @property
    def library(self):
        return self._shell.library

    @property
    def screen(self):
        return self.stack[-1] if self.stack else None

    def push(self, screen):
        self.stack.append(screen)

    def pop(self):
        if len(self.stack) > 1:
            self.stack.pop()
            top = self.screen
            if top is not None and hasattr(top, "on_resume"):
                try:
                    top.on_resume()
                except Exception as exc:                # noqa: BLE001
                    print(f"control on_resume failed: {exc!r}", flush=True)
        else:
            self._shell.pop()        # leaving the Control root → back to the game

    def request_exit(self):
        self._shell.pop()

    def toast(self, text, ms: int = 2200):
        self._shell.toast(text, ms)

    def request_remap(self):
        # The raw remap is the ONE thing that needs gptokeyb OFF. Keep the app
        # seamless otherwise: leave a flag + exit so launch.sh briefly relaunches
        # THIS shell in --remap mode (no gptokeyb), then returns to the game.
        try:
            with open(os.path.join(os.getcwd(), ".open-remap"), "w") as fh:
                fh.write("1")
        except Exception as exc:                        # noqa: BLE001
            print(f"request_remap failed: {exc!r}", flush=True)
        self._shell.toast("Opening controller remap…")
        self._shell.request_exit()


class ControlHost:
    name = "control"

    def __init__(self, app):
        self.app = app
        self._bg = self._build_bg()
        self.capp = _ControlApp(app)
        from ..control.screens.home import HomeScreen as ControlHome
        self.capp.push(ControlHome(self.capp))

    def _build_bg(self):
        w, h = ct.VIEWPORT_WIDTH, ct.VIEWPORT_HEIGHT
        try:
            bg = crender.radial_gradient(
                (w, h), (0.30, 0.20), ct.Color.BG_UP, ct.Color.BG_DEEP).convert()
        except Exception:                               # noqa: BLE001
            bg = pygame.Surface((w, h))
            bg.fill(ct.Color.BG_DEEP)
        # faint mathematical grid, in the brand accents (matches AAhead Control)
        grid = pygame.Surface((w, h), pygame.SRCALPHA)
        for x in range(0, w, 40):
            pygame.draw.line(grid, (125, 206, 221, 10), (x, 0), (x, h))
        for y in range(0, h, 40):
            pygame.draw.line(grid, (232, 159, 211, 8), (0, y), (w, y))
        bg.blit(grid, (0, 0))
        return bg

    def on_action(self, action):
        scr = self.capp.screen
        if scr is None:
            self.app.pop()
            return
        scr.on_action(action)

    def update(self, dt_ms):
        scr = self.capp.screen
        if scr is not None:
            scr.update(dt_ms)

    def draw(self, surf):
        surf.blit(self._bg, (0, 0))
        scr = self.capp.screen
        if scr is not None:
            scr.draw(surf)
