"""Bottom-right archive badge overlay during fullscreen video playback."""
from __future__ import annotations

import os
import time

import xbmc
import xbmcgui
import xbmcvfs

from . import logger
from .archive_marker import clear_archive_playback, is_antik_archive_playback
from .constants import ADDON_ID, OVERLAY_SERVICE_LOCK_PROP
from .settings_sync import read_show_archive_badge_live

_FULLSCREEN_VIDEO = 12005
_HOME = xbmcgui.Window(10000)
_ICON_REL = "resources/media/archive-badge.png"
_ICON_REF_PX = 64
_REF_WIDTH = 1920.0
_LOCK_STALE_SECONDS = 30.0
# Brief pause after fullscreen activates so VideoFullScreen.xml can load.
_FULLSCREEN_SETTLE_SECONDS = 0.4
_OSD_VISIBLE = (
    "Window.IsVisible(videoosd) | "
    "Window.IsVisible(OSDVideoSettings) | "
    "Window.IsActive(VideoOSD) | "
    "Player.ShowInfo"
)


def _osd_open() -> bool:
    return bool(xbmc.getCondVisibility(_OSD_VISIBLE))


def _fullscreen_video_active() -> bool:
    return bool(xbmc.getCondVisibility("Window.IsActive(fullscreenvideo)"))


def _layout(window: xbmcgui.Window) -> tuple[int, int, int, int]:
    """Bottom-right placement in the fullscreen window coordinate space."""
    width = max(int(window.getWidth()), 0)
    height = max(int(window.getHeight()), 0)
    if width <= 0 or height <= 0:
        width, height = 1920, 1080

    scale = width / _REF_WIDTH
    size = max(32, int(_ICON_REF_PX * scale))
    pad_x = max(16, int(width / 20))
    pad_y = max(16, int(height / 15))
    x = max(0, width - size - pad_x)
    y = max(0, height - size - pad_y)
    return x, y, size, size


def _fullscreen_window() -> xbmcgui.Window | None:
    try:
        return xbmcgui.Window(_FULLSCREEN_VIDEO)
    except Exception:
        return None


class ArchiveOverlay:
    def __init__(self) -> None:
        self._owns_service_lock = False
        self._control: xbmcgui.ControlImage | None = None
        self._shown = False
        self._fullscreen_since = 0.0
        self._fullscreen_settled = False
        self._badge_enabled = read_show_archive_badge_live()

    @staticmethod
    def _icon_path() -> str:
        raw = f"special://home/addons/{ADDON_ID}/{_ICON_REL}"
        return xbmcvfs.translatePath(raw)

    @staticmethod
    def _parse_lock_token(token: str) -> tuple[str, float]:
        if not token:
            return "", 0.0
        parts = token.split(":", 1)
        owner_pid = parts[0]
        try:
            owner_ts = float(parts[1]) if len(parts) > 1 else 0.0
        except ValueError:
            owner_ts = 0.0
        return owner_pid, owner_ts

    def acquire_service_lock(self) -> bool:
        now = time.time()
        my_pid = str(os.getpid())
        my_token = f"{my_pid}:{now:.0f}"
        existing = _HOME.getProperty(OVERLAY_SERVICE_LOCK_PROP) or ""
        owner_pid, owner_ts = self._parse_lock_token(existing)
        if (
            owner_pid
            and owner_pid != my_pid
            and (now - owner_ts) < _LOCK_STALE_SECONDS
        ):
            logger.warning(
                f"Archive overlay service lock held by pid {owner_pid}; standby"
            )
            return False

        _HOME.setProperty(OVERLAY_SERVICE_LOCK_PROP, my_token)
        self._owns_service_lock = True
        return True

    def refresh_service_lock(self) -> None:
        if not self._owns_service_lock:
            return
        _HOME.setProperty(
            OVERLAY_SERVICE_LOCK_PROP,
            f"{os.getpid()}:{time.time():.0f}",
        )

    def release_service_lock(self) -> None:
        if not self._owns_service_lock:
            return
        token = _HOME.getProperty(OVERLAY_SERVICE_LOCK_PROP) or ""
        owner_pid, _ = self._parse_lock_token(token)
        if owner_pid == str(os.getpid()):
            _HOME.clearProperty(OVERLAY_SERVICE_LOCK_PROP)
        self._owns_service_lock = False

    def has_service_lock(self) -> bool:
        return self._owns_service_lock

    def _reset_fullscreen_timer(self) -> None:
        self._fullscreen_since = 0.0
        self._fullscreen_settled = False

    def _fullscreen_ready_to_draw(self, window: xbmcgui.Window) -> bool:
        if not _fullscreen_video_active():
            self._reset_fullscreen_timer()
            return False

        if window.getWidth() <= 0 or window.getHeight() <= 0:
            self._reset_fullscreen_timer()
            return False

        if self._fullscreen_settled:
            return True

        now = time.time()
        if self._fullscreen_since <= 0:
            self._fullscreen_since = now
            return False

        if now - self._fullscreen_since >= _FULLSCREEN_SETTLE_SECONDS:
            self._fullscreen_settled = True
            return True

        return False

    def _destroy_control(self) -> None:
        control = self._control
        self._control = None
        self._shown = False
        if control is None:
            return

        try:
            control.setVisible(False)
        except Exception:
            pass

        try:
            window = _fullscreen_window()
            if window is not None:
                window.removeControl(control)
        except Exception:
            pass

    def _apply_badge_setting(self, enabled: bool) -> None:
        if enabled == self._badge_enabled:
            if not enabled and (self._shown or self._control is not None):
                self.hide(destroy=True)
            return

        self._badge_enabled = enabled
        logger.info(
            f"Archive badge setting changed: {'enabled' if enabled else 'disabled'}"
        )
        self.hide(destroy=True)

    def _sync_setting_state(self) -> None:
        self._apply_badge_setting(read_show_archive_badge_live())

    def hide(self, *, destroy: bool = False, reset_fullscreen: bool = False) -> None:
        if reset_fullscreen:
            self._reset_fullscreen_timer()

        if destroy:
            self._destroy_control()
            return

        if self._control is not None and self._shown:
            try:
                self._control.setVisible(False)
            except Exception:
                self._destroy_control()
                return
        self._shown = False

    def show(self) -> None:
        if not self._owns_service_lock:
            return
        if not self._badge_enabled:
            return
        if _osd_open():
            return
        if not xbmc.getCondVisibility("Player.Playing"):
            return

        window = _fullscreen_window()
        if window is None:
            return
        if not self._fullscreen_ready_to_draw(window):
            return

        if self._control is not None:
            try:
                self._control.setVisible(True)
                self._shown = True
                return
            except Exception:
                self._destroy_control()

        icon_path = self._icon_path()
        if not xbmcvfs.exists(icon_path):
            logger.warning(f"Archive badge image missing: {icon_path}")
            return

        try:
            x, y, w, h = _layout(window)
            control = xbmcgui.ControlImage(x, y, w, h, icon_path, aspectRatio=2)
            control.setEnabled(False)
            control.setVisible(True)
            window.addControl(control)
            self._control = control
            self._shown = True
            logger.info(
                f"Archive overlay shown at {x},{y} size {w}x{h} "
                f"(window {window.getWidth()}x{window.getHeight()})"
            )
        except Exception as exc:
            logger.info(f"Archive overlay show failed: {exc}")
            self._destroy_control()

    def on_settings_changed(self) -> None:
        self._apply_badge_setting(read_show_archive_badge_live())
        logger.info(
            f"Archive badge settings updated: "
            f"{'enabled' if self._badge_enabled else 'disabled'}"
        )

    def on_playback_started(self, player: xbmc.Player) -> None:
        if not is_antik_archive_playback(player):
            self.hide(destroy=True, reset_fullscreen=True)
            clear_archive_playback()
            return
        self.hide(destroy=True, reset_fullscreen=True)

    def tick(self, player: xbmc.Player) -> None:
        if not self._owns_service_lock:
            return

        self.refresh_service_lock()
        self._sync_setting_state()

        if not player.isPlayingVideo():
            if self._shown or self._control is not None:
                self.hide(destroy=True, reset_fullscreen=True)
            return

        if not is_antik_archive_playback(player):
            if self._shown or self._control is not None:
                self.hide(destroy=True)
            playing = player.getPlayingFile() or ""
            if playing:
                clear_archive_playback()
            return

        if not self._badge_enabled:
            if self._shown or self._control is not None:
                self.hide(destroy=True)
            return

        if _osd_open():
            if self._shown:
                self.hide(destroy=False)
            return

        if not self._shown:
            self.show()
