from __future__ import annotations

import json
import os
import sys
import traceback
from pathlib import Path

from .console_instance_lock import (
    ConsoleInstanceLock,
    consume_console_activation_request,
    duplicate_instance_exit_code,
    install_console_lock_cleanup,
    read_running_console_pid,
    try_acquire_console_or_raise_existing,
)
from .console_window_raise import (
    collect_console_status,
    format_console_status_text,
    is_console_running,
    raise_existing_console_window,
)
from .main_window import apply_application_branding, create_main_window
from .profile_language import resolve_ui_language
from .i18n import translate
from .agent_status_overlay import bind_agent_status_overlay
from .startup_splash import StartupSplashController
from .status_adapter import StatusAdapter

CONSOLE_APPLICATION_ID = "eu.iuve.connect.agent.console"
CONSOLE_WM_CLASS = "iuve-connect-agent"


def _emit_console_event(payload: dict) -> None:
    """Structured console diagnostics on stderr (visible when launched from a terminal)."""

    envelope = {"ok": True, **payload}
    print(json.dumps(envelope, ensure_ascii=True), file=sys.stderr)


def _emit_console_error(
    error: str,
    *,
    detail: str = "",
    hint: str = "",
    **extra: object,
) -> None:
    payload: dict[str, object] = {"ok": False, "error": error}
    if detail:
        payload["detail"] = detail
    if hint:
        payload["hint"] = hint
    payload.update(extra)
    print(json.dumps(payload, ensure_ascii=True), file=sys.stderr)


def _emit_console_stdout(payload: dict) -> None:
    print(json.dumps(payload, ensure_ascii=True))


def _handle_existing_console_instance() -> int:
    pid = read_running_console_pid() or 0
    ok, _details = raise_existing_console_window(
        pid=pid,
        application_id=CONSOLE_APPLICATION_ID,
        wm_class=CONSOLE_WM_CLASS,
    )
    if ok:
        _emit_console_stdout(
            {
                "ok": True,
                "action": "reused_existing_instance",
                "window_raised": True,
                "application_id": CONSOLE_APPLICATION_ID,
                "message": "Existing console activated.",
            }
        )
        return 0
    _emit_console_stdout(
        {
            "ok": False,
            "error": "window_raise_failed",
            "console_running": True,
            "pid": pid,
            "hint": "Console is already running but could not be focused.",
        }
    )
    return duplicate_instance_exit_code()


def run_console_focus(*, config_path: str | Path) -> int:
    """Raise an existing console window without starting a new instance."""
    _ = config_path
    running, _pid = is_console_running()
    if not running:
        _emit_console_stdout(
            {
                "ok": False,
                "error": "console_not_running",
                "hint": "Start the console with: iuve-connect-desktop-agent console",
            }
        )
        return 1
    return _handle_existing_console_instance()


def run_console_status(*, config_path: str | Path, json_output: bool = False) -> int:
    """Report console process/window/backend status for scripting or humans."""
    status = collect_console_status(config_path=config_path)
    if json_output:
        _emit_console_stdout({"ok": True, **status})
    else:
        print(format_console_status_text(status))
    return 0


def run_console(*, config_path: str | Path) -> int:
    """Run GTK4/libadwaita console UI for the desktop agent."""

    display = str(os.environ.get("DISPLAY", "")).strip()
    wayland_display = str(os.environ.get("WAYLAND_DISPLAY", "")).strip()
    if not display and not wayland_display:
        print(
            json.dumps(
                {
                    "ok": False,
                    "error": "display_session_unavailable",
                    "detail": "Neither DISPLAY nor WAYLAND_DISPLAY is set.",
                    "hint": "Run UI from a graphical desktop session (KDE/GNOME), not TTY/SSH.",
                },
                ensure_ascii=True,
            )
        )
        return 1

    try:
        import gi  # type: ignore

        gi.require_version("Gtk", "4.0")
        gi.require_version("Adw", "1")
        gi.require_version("Gio", "2.0")
        from gi.repository import Adw, GLib, Gio, Gdk  # type: ignore
    except Exception as exc:
        print(
            json.dumps(
                {
                    "ok": False,
                    "error": "gtk4_libadwaita_unavailable",
                    "detail": str(exc),
                },
                ensure_ascii=True,
            )
        )
        return 2

    console_lock: ConsoleInstanceLock | None = try_acquire_console_or_raise_existing(
        on_duplicate=lambda: None,
    )
    if console_lock is None:
        return _handle_existing_console_instance()

    install_console_lock_cleanup(console_lock)

    agent_status_overlay = None

    try:
        status_adapter = StatusAdapter(config_path=config_path)
    except Exception as exc:
        console_lock.release()
        _emit_console_error(
            "console_status_adapter_init_failed",
            detail=str(exc),
            hint="Check desktop-config.json and agent storage paths.",
        )
        return 3

    _emit_console_event(
        {
            "event": "console_startup",
            "application_id": CONSOLE_APPLICATION_ID,
            "display": display or None,
            "wayland_display": wayland_display or None,
            "xdg_session_type": str(os.environ.get("XDG_SESSION_TYPE", "")).strip() or None,
        }
    )

    try:
        from gi.repository import Gtk  # type: ignore

        agent_status_overlay = bind_agent_status_overlay(
            gtk_module=Gtk,
            glib_module=GLib,
            resolve_lang=lambda: resolve_ui_language(
                profile_lang=status_adapter.fetch_owner_interface_language(),
                cached_lang=status_adapter.load_cached_ui_language(),
                env_override=str(os.environ.get("IUVE_AGENT_UI_LANG", "")).strip(),
            ),
        )
    except Exception as exc:
        _emit_console_event(
            {
                "event": "agent_status_overlay_init_skipped",
                "detail": str(exc),
            }
        )

    class ConsoleApplication(Adw.Application):
        def __init__(self) -> None:
            super().__init__(
                application_id=CONSOLE_APPLICATION_ID,
                flags=Gio.ApplicationFlags.NON_UNIQUE,
            )
            self._window = None
            self._hold_active = False
            self._splash: StartupSplashController | None = None

        def do_startup(self) -> None:
            Adw.Application.do_startup(self)
            try:
                Gdk.set_program_class(CONSOLE_WM_CLASS)
            except Exception:
                pass
            try:
                GLib.set_application_name("IuVe Connect Agent")
            except Exception:
                pass
            apply_application_branding(self)

        def _ensure_hold(self) -> None:
            if not self._hold_active:
                self.hold()
                self._hold_active = True

        def _release_hold(self) -> None:
            if self._hold_active:
                self.release()
                self._hold_active = False

        def _present_main_window(self) -> None:
            if self._window is not None:
                self._window.present()
                _emit_console_event(
                    {
                        "event": "console_window_presented",
                        "application_id": CONSOLE_APPLICATION_ID,
                    }
                )
                return

            from gi.repository import Gtk  # type: ignore

            try:
                self._window = create_main_window(
                    adw_module=Adw,
                    gtk_module=Gtk,
                    glib_module=GLib,
                    application=self,
                    status_adapter=status_adapter,
                )
            except Exception as exc:
                _emit_console_error(
                    "console_window_init_failed",
                    detail=str(exc),
                    traceback=traceback.format_exc(),
                )
                self.quit()
                return

            self._window.connect("close-request", self._on_window_close_request)
            self._window.connect("destroy", self._on_window_destroy)
            _emit_console_event(
                {
                    "event": "console_window_created",
                    "application_id": CONSOLE_APPLICATION_ID,
                    "default_size": "1020x680",
                    "startup_wm_class": CONSOLE_WM_CLASS,
                }
            )
            self._window.present()
            _emit_console_event(
                {
                    "event": "console_window_presented",
                    "application_id": CONSOLE_APPLICATION_ID,
                }
            )

        def do_activate(self) -> None:
            self._ensure_hold()
            if self._window is not None:
                self._present_main_window()
                return
            if self._splash is not None:
                return

            from gi.repository import Gtk  # type: ignore

            lang = resolve_ui_language(
                profile_lang=None,
                cached_lang=status_adapter.load_cached_ui_language(),
                env_override=str(os.environ.get("IUVE_AGENT_UI_LANG", "")).strip(),
            )

            def _open_main_window() -> None:
                if self._splash is not None:
                    self._splash.destroy()
                    self._splash = None
                self._present_main_window()

            self._splash = StartupSplashController(
                status_adapter=status_adapter,
                gtk_module=Gtk,
                adw_module=Adw,
                glib_module=GLib,
                translate_fn=lambda key: translate(lang, key),
            )
            self._splash.present(_open_main_window)

        def _poll_console_activation(self) -> bool:
            if consume_console_activation_request():
                self._present_main_window()
            return True

        def _on_window_destroy(self, _window) -> None:
            if self._window is _window:
                self._window = None

        def _on_window_close_request(self, _window) -> bool:
            try:
                status_adapter.revoke_app_exit_permission_grants()
            except Exception:
                pass
            if agent_status_overlay is not None:
                try:
                    agent_status_overlay.shutdown()
                except Exception:
                    pass
            self._release_hold()
            self._window = None
            self.quit()
            return True

    try:
        app = ConsoleApplication()
        GLib.timeout_add(500, app._poll_console_activation)
        exit_code = int(app.run([]))
        _emit_console_event(
            {
                "event": "console_shutdown",
                "application_id": CONSOLE_APPLICATION_ID,
                "exit_code": exit_code,
            }
        )
        return exit_code
    except Exception as exc:
        _emit_console_error(
            "console_run_failed",
            detail=str(exc),
            traceback=traceback.format_exc(),
        )
        return 4
    finally:
        console_lock.release()
