"""Сохранение размера/позиции окна — восстановление после трея и toast."""

from __future__ import annotations

import json
import logging
import threading
from typing import TYPE_CHECKING, Any, Callable

from paths import get_panel_data_dir

if TYPE_CHECKING:
    import webview

logger = logging.getLogger(__name__)

STATE_PATH = get_panel_data_dir() / "window_state.json"

_DEFAULT: dict[str, Any] = {
    "width": 1100,
    "height": 720,
    "x": None,
    "y": None,
    "maximized": False,
    "fullscreen": False,
}

_restoring = False
_shutting_down = False
_pending_timers: list[threading.Timer] = []
SW_MAXIMIZE = 3
SW_SHOW = 5


def mark_shutting_down() -> None:
    global _shutting_down
    _shutting_down = True
    cancel_pending_timers()


def cancel_pending_timers() -> None:
    for timer in _pending_timers:
        try:
            timer.cancel()
        except Exception:
            pass
    _pending_timers.clear()


def is_shutting_down() -> bool:
    return _shutting_down


def load_window_state() -> dict[str, Any]:
    if not STATE_PATH.exists():
        return dict(_DEFAULT)
    try:
        data = json.loads(STATE_PATH.read_text(encoding="utf-8"))
        out = dict(_DEFAULT)
        out.update({k: data.get(k, out[k]) for k in out})
        out["width"] = max(int(out.get("width") or 1100), 960)
        out["height"] = max(int(out.get("height") or 720), 640)
        return out
    except Exception:
        return dict(_DEFAULT)


def save_window_state(state: dict[str, Any]) -> None:
    try:
        STATE_PATH.parent.mkdir(parents=True, exist_ok=True)
        STATE_PATH.write_text(json.dumps(state, ensure_ascii=False, indent=2), encoding="utf-8")
    except Exception as e:
        logger.warning("save window state: %s", e)


def _get_form(window: "webview.Window"):
    try:
        from webview.platforms import winforms as wf

        return wf.BrowserView.instances.get(window.uid)
    except Exception:
        return None


def _run_on_ui(window: "webview.Window", fn: Callable[[], None]) -> None:
    if _shutting_down:
        return
    form = _get_form(window)
    if not form:
        try:
            fn()
        except Exception as e:
            logger.warning("ui callback (no form): %s", e)
        return
    try:
        from System import Action

        if form.InvokeRequired:
            # BeginInvoke — не блокируем фоновый поток (Invoke давал deadlock при закрытии).
            form.BeginInvoke(Action(fn))
        else:
            fn()
    except Exception as e:
        logger.warning("ui invoke: %s", e)
        try:
            fn()
        except Exception:
            pass


def run_on_ui_sync(window: "webview.Window", fn: Callable[[], None]) -> None:
    """Синхронно на UI-потоке — для показа окна из трея/toast."""
    if _shutting_down:
        return
    form = _get_form(window)
    if not form:
        try:
            fn()
        except Exception as e:
            logger.warning("ui sync (no form): %s", e)
        return
    try:
        from System import Action

        if form.InvokeRequired:
            form.Invoke(Action(fn))
        else:
            fn()
    except Exception as e:
        logger.warning("ui sync: %s", e)
        try:
            fn()
        except Exception:
            pass


def _bring_to_front(form) -> None:
    try:
        import ctypes

        hwnd = form.Handle.ToInt32()
        ctypes.windll.user32.ShowWindow(hwnd, SW_SHOW)
        ctypes.windll.user32.SetForegroundWindow(hwnd)
    except Exception as e:
        logger.warning("bring to front: %s", e)


def hide_window_to_tray_form(window: "webview.Window") -> None:
    """Только UI-поток: спрятать окно и убрать из панели задач."""
    form = _get_form(window)
    if form:
        try:
            form.ShowInTaskbar = False
        except Exception:
            pass
    try:
        if form:
            form.Hide()
        else:
            window.hide()
    except Exception as e:
        logger.warning("hide to tray: %s", e)


def show_window_from_tray(window: "webview.Window") -> None:
    """Синхронно показать окно — трей, toast, панель задач."""
    if _shutting_down:
        return
    try:
        from tray_service import is_hiding_to_tray

        if is_hiding_to_tray():
            return
    except Exception:
        pass
    state = load_window_state()

    def _show() -> None:
        form = _get_form(window)
        if form:
            try:
                form.ShowInTaskbar = True
            except Exception:
                pass
        try:
            _apply_state_on_form(window, state)
        except Exception as e:
            logger.warning("show from tray: %s", e)
            try:
                window.show()
            except Exception:
                pass
        form = _get_form(window)
        if form:
            _bring_to_front(form)

    run_on_ui_sync(window, _show)
    _schedule_reapply(window, state, 0.2)
    _schedule_reapply(window, state, 0.55)


def defer_on_ui(window: "webview.Window", fn: Callable[[], None]) -> None:
    """Выполнить на UI-потоке WinForms (асинхронно)."""
    _run_on_ui(window, fn)


def post_on_ui(window: "webview.Window", fn: Callable[[], None]) -> None:
    """Всегда отложить на следующий тик UI (даже если уже на UI-потоке)."""
    if _shutting_down:
        return
    form = _get_form(window)
    if not form:
        threading.Timer(0.05, fn).start()
        return
    try:
        from System import Action

        form.BeginInvoke(Action(fn))
    except Exception as e:
        logger.warning("post_on_ui: %s", e)
        threading.Timer(0.05, fn).start()


def _winforms_runtime(window: "webview.Window") -> dict[str, bool]:
    form = _get_form(window)
    if not form:
        return {}
    try:
        import System.Windows.Forms as WinForms

        ws = form.WindowState
        return {
            "maximized": ws == WinForms.FormWindowState.Maximized,
            "fullscreen": bool(getattr(form, "is_fullscreen", False)),
        }
    except Exception:
        return {}


def bind_window_state(window: "webview.Window") -> None:
    state = load_window_state()

    def _merge(**kwargs: Any) -> None:
        state.update(kwargs)
        save_window_state(state)

    def on_resized(w: int, h: int) -> None:
        if _restoring or state.get("maximized") or state.get("fullscreen"):
            return
        _merge(width=int(w), height=int(h))

    def on_moved(x: int, y: int) -> None:
        if _restoring or state.get("maximized") or state.get("fullscreen"):
            return
        _merge(x=int(x), y=int(y))

    def on_maximized() -> None:
        _merge(maximized=True, fullscreen=False)

    def on_restored() -> None:
        if _restoring:
            return
        _merge(maximized=False, fullscreen=False)

    window.events.resized += on_resized
    window.events.moved += on_moved
    window.events.maximized += on_maximized
    window.events.restored += on_restored


def snapshot_window(window: "webview.Window") -> None:
    state = load_window_state()
    try:
        runtime = _winforms_runtime(window)
        if runtime.get("fullscreen"):
            state["fullscreen"] = True
            state["maximized"] = False
        elif runtime.get("maximized"):
            state["maximized"] = True
            state["fullscreen"] = False
        else:
            state["maximized"] = False
            state["fullscreen"] = False
            state["width"] = int(getattr(window, "width", state["width"]) or state["width"])
            state["height"] = int(getattr(window, "height", state["height"]) or state["height"])
        save_window_state(state)
    except Exception as e:
        logger.warning("snapshot window: %s", e)


def _apply_state_on_form(window: "webview.Window", state: dict[str, Any]) -> None:
    global _restoring
    form = _get_form(window)
    if not form:
        return
    _restoring = True
    try:
        import ctypes
        import System.Windows.Forms as WinForms

        form.Show()
        form.Activate()
        hwnd = form.Handle.ToInt32()

        if state.get("fullscreen"):
            if not getattr(form, "is_fullscreen", False):
                try:
                    window.toggle_fullscreen()
                except Exception:
                    pass
            return

        if state.get("maximized"):
            form.WindowState = WinForms.FormWindowState.Maximized
            ctypes.windll.user32.ShowWindow(hwnd, SW_MAXIMIZE)
            return

        form.WindowState = WinForms.FormWindowState.Normal
        w = max(int(state.get("width") or 1100), 960)
        h = max(int(state.get("height") or 720), 640)
        form.Size = WinForms.Size(w, h)
        x, y = state.get("x"), state.get("y")
        if x is not None and y is not None:
            form.Location = WinForms.Point(int(x), int(y))
        ctypes.windll.user32.ShowWindow(hwnd, SW_SHOW)
    except Exception as e:
        logger.warning("apply window state: %s", e)
    finally:
        _restoring = False


def _schedule_reapply(window: "webview.Window", state: dict[str, Any], delay: float) -> None:
    if _shutting_down or not (state.get("maximized") or state.get("fullscreen")):
        return

    def _retry() -> None:
        if _shutting_down:
            return
        try:
            from panel_state import is_panel_hidden

            if is_panel_hidden():
                return
        except Exception:
            pass
        _run_on_ui(window, lambda: _apply_state_on_form(window, state))

    timer = threading.Timer(delay, _retry)
    _pending_timers.append(timer)
    timer.start()


def present_window(window: "webview.Window") -> None:
    """Показать окно из трея/toast."""
    show_window_from_tray(window)


def restore_window(window: "webview.Window") -> None:
    present_window(window)
