"""Cross-platform system tray icon using pystray.

Creates the tray icon with a menu:
  - Process (hotkey trigger)
  - Settings
  - Statistics (optional)
  - Separator
  - Quit
"""

from __future__ import annotations

import logging
import sys
import threading
from collections.abc import Callable
from pathlib import Path

from ..i18n import _

log = logging.getLogger(__name__)

_ASSETS_DIR = Path(__file__).parent.parent.parent / "assets"


def _load_icon(theme: str = "dark"):
    """Load the tray icon image for the given theme. Returns a PIL Image.

    Args:
        theme: 'light' or 'dark'. Use 'dark' icon for dark OS theme (light icon),
               use 'light' icon for light OS theme (dark icon).
    """
    try:
        from PIL import Image

        # Prefer themed icons from assets/icons/
        themed_path = _ASSETS_DIR / "icons" / f"icon_{theme}.png"
        if themed_path.exists():
            return Image.open(themed_path)

        # Fall back to platform-specific default icon
        if sys.platform == "win32":
            icon_path = _ASSETS_DIR / "icon.ico"
        else:
            icon_path = _ASSETS_DIR / "icon.png"

        if icon_path.exists():
            return Image.open(icon_path)

        return _make_placeholder_icon(theme)
    except Exception as exc:
        log.warning("Failed to load icon: %s — using placeholder", exc)
        return _make_placeholder_icon(theme)


def _make_placeholder_icon(theme: str = "dark"):
    """Generate a simple placeholder icon for the given theme."""
    from PIL import Image, ImageDraw

    img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
    draw = ImageDraw.Draw(img)
    if theme == "dark":
        # Light icon for dark tray background
        draw.ellipse([8, 8, 56, 56], fill=(220, 220, 220, 255))
        draw.text((20, 20), "AI", fill=(30, 30, 30, 255))
    else:
        # Dark icon for light tray background
        draw.ellipse([8, 8, 56, 56], fill=(50, 120, 220, 255))
        draw.text((20, 20), "AI", fill=(255, 255, 255, 255))
    return img


class TrayIcon:
    """System tray icon with context menu."""

    def __init__(
        self,
        on_process: Callable[[], None],
        on_settings: Callable[[], None],
        on_quit: Callable[[], None],
        on_stats: Callable[[], None] | None = None,
        on_prompts: Callable[[], None] | None = None,
        on_debug_screenshot: Callable[[], None] | None = None,
        on_mode_switch: Callable[[str], None] | None = None,
        on_debug_toggle: Callable[[], None] | None = None,
        on_activate: Callable[[], None] | None = None,
        hotkey_label: str = "Ctrl+Shift+Space",
        stealth: bool = False,
        is_trial: bool = False,
    ) -> None:
        self._on_process = on_process
        self._on_settings = on_settings
        self._on_quit = on_quit
        self._on_stats = on_stats
        self._on_prompts = on_prompts
        self._on_debug_screenshot = on_debug_screenshot
        self._on_mode_switch = on_mode_switch
        self._on_debug_toggle = on_debug_toggle
        self._on_activate = on_activate
        self._hotkey_label = hotkey_label
        self._stealth = stealth
        self._is_trial = is_trial
        self._icon = None
        self._busy = False
        self._icon_frames: list = []
        self._anim_thread: threading.Thread | None = None
        self._anim_stop = threading.Event()
        self._current_mode = "math"

    def run(self) -> None:
        """Start the tray icon — blocks until quit."""
        import pystray

        # Clean tray: Console, Mode, Quit
        items = []

        # Console — opens console window for logs
        items.append(pystray.MenuItem("Console", self._handle_console))

        # Mode submenu
        if self._on_mode_switch:
            mode_menu = pystray.Menu(
                pystray.MenuItem(
                    "Mathematics",
                    self._handle_mode_math,
                    checked=lambda item: self._current_mode == "math",
                ),
                pystray.MenuItem(
                    "Physics",
                    self._handle_mode_physics,
                    checked=lambda item: self._current_mode == "physics",
                ),
                pystray.MenuItem(
                    "Chemistry",
                    self._handle_mode_chemistry,
                    checked=lambda item: self._current_mode == "chemistry",
                ),
                pystray.MenuItem(
                    "Chinese",
                    self._handle_mode_chinese,
                    checked=lambda item: self._current_mode == "chinese",
                ),
            )
            items.append(pystray.MenuItem("Mode", mode_menu))

        # Buy & Activate (only on trial)
        if self._is_trial:
            items.append(pystray.Menu.SEPARATOR)
            items.append(pystray.MenuItem("Buy License", self._handle_buy))
            if self._on_activate:
                items.append(pystray.MenuItem("Activate License", self._handle_activate))

        # Open Terminal in app folder
        items.append(pystray.MenuItem("Terminal", self._handle_terminal))

        # Debug toggle
        if self._on_debug_toggle:
            items.append(pystray.MenuItem("Debug", self._handle_debug))

        items += [
            pystray.Menu.SEPARATOR,
            pystray.MenuItem("Quit", self._handle_quit),
        ]
        menu = pystray.Menu(*items)

        # Load icon for current theme
        initial_theme = "dark"
        try:
            from .theme_manager import ThemeManager

            initial_theme = ThemeManager.get_instance().get_current_theme()
        except (RuntimeError, ImportError):
            pass

        if self._stealth:
            tray_name = "Windows Audio Device Graph"
            tray_title = "Windows Audio"
        else:
            tray_name = "MuraveyChina"
            tray_title = "MuraveyChina AI"
            # Show license status in tooltip
            try:
                from .license_v2_client import LicenseV2Client
                # Try reading cached v2 data
                import json
                cache_path = __import__("pathlib").Path.home() / ".muravey-china" / "license_v2.json"
                if cache_path.exists():
                    wrapper = json.loads(cache_path.read_text())
                    data = json.loads(wrapper.get("data", "{}"))
                    status = data.get("status", "")
                    if status == "paid":
                        paid_until = data.get("paid_access_until", "")
                        tray_title = f"MuraveyChina — Paid until {paid_until}"
                    elif status == "free":
                        cutoff = data.get("free_cutoff_date", "")
                        tray_title = f"MuraveyChina — Free until {cutoff}"
                    else:
                        tray_title = "MuraveyChina — Expired"
            except Exception:
                pass

        self._icon = pystray.Icon(
            name=tray_name,
            icon=_load_icon(initial_theme),
            title=tray_title,
            menu=menu,
        )
        self._current_theme = initial_theme

        log.debug("Tray icon started")
        assert self._icon is not None
        self._icon.run()

    def set_busy(self, busy: bool) -> None:
        """Show/hide busy animation in the tray."""
        self._busy = busy
        if busy:
            self._start_animation()
        else:
            self._stop_animation()

    def show_notification(self, title: str, message: str) -> None:
        """Show a balloon notification (Windows) or similar."""
        if self._icon:
            try:
                self._icon.notify(message, title)
            except Exception:
                pass  # not supported on all platforms

    def update_icon_theme(self, theme: str) -> None:
        """Switch the tray icon to match the given theme ('light' or 'dark')."""
        self._current_theme = theme
        if self._icon and not self._busy:
            try:
                self._icon.icon = _load_icon(theme)
                log.debug("Tray icon updated for theme: %s", theme)
            except Exception as exc:
                log.warning("Failed to update tray icon: %s", exc)

    def stop(self) -> None:
        """Stop and remove the tray icon."""
        self._stop_animation()
        if self._icon:
            self._icon.stop()

    # ------------------------------------------------------------------ #
    # Menu handlers
    # ------------------------------------------------------------------ #

    def _handle_process(self, icon, item) -> None:
        threading.Thread(target=self._on_process, daemon=True).start()

    def _handle_settings(self, icon, item) -> None:
        threading.Thread(target=self._on_settings, daemon=True).start()

    def _handle_prompts(self, icon, item) -> None:
        if self._on_prompts:
            threading.Thread(target=self._on_prompts, daemon=True).start()

    def _handle_stats(self, icon, item) -> None:
        if self._on_stats:
            threading.Thread(target=self._on_stats, daemon=True).start()

    def _handle_debug_screenshot(self, icon, item) -> None:
        if self._on_debug_screenshot:
            threading.Thread(target=self._on_debug_screenshot, daemon=True).start()

    def _handle_mode_math(self, icon, item) -> None:
        self._current_mode = "math"
        if self._on_mode_switch:
            self._on_mode_switch("math")
        if self._icon:
            self._icon.update_menu()

    def _handle_mode_physics(self, icon, item) -> None:
        self._current_mode = "physics"
        if self._on_mode_switch:
            self._on_mode_switch("physics")
        if self._icon:
            self._icon.update_menu()

    def _handle_mode_chemistry(self, icon, item) -> None:
        self._current_mode = "chemistry"
        if self._on_mode_switch:
            self._on_mode_switch("chemistry")
        if self._icon:
            self._icon.update_menu()

    def _handle_mode_chinese(self, icon, item) -> None:
        self._current_mode = "chinese"
        if self._on_mode_switch:
            self._on_mode_switch("chinese")
        if self._icon:
            self._icon.update_menu()

    # Legacy aliases
    def _handle_mode_stem(self, icon, item) -> None:
        self._handle_mode_chinese(icon, item)

    def _handle_mode_humanities(self, icon, item) -> None:
        self._handle_mode_chinese(icon, item)

    _console_attached = False

    def _handle_console(self, icon, item) -> None:
        """Attach a real console window to the current Muravey process."""
        import sys, os, logging
        if sys.platform != "win32":
            return
        import ctypes

        if TrayIcon._console_attached:
            # Console already open — just bring it to front
            hwnd = ctypes.windll.kernel32.GetConsoleWindow()
            if hwnd:
                ctypes.windll.user32.ShowWindow(hwnd, 9)  # SW_RESTORE
                ctypes.windll.user32.SetForegroundWindow(hwnd)
            return

        # Allocate a new console for THIS process (pythonw has none)
        ctypes.windll.kernel32.AllocConsole()
        ctypes.windll.kernel32.SetConsoleTitleW("Muravey Console")

        # Redirect Python stdout/stderr to the new console
        sys.stdout = open("CONOUT$", "w", encoding="utf-8")
        sys.stderr = open("CONOUT$", "w", encoding="utf-8")

        # Add a logging handler that writes to the new console
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setLevel(logging.INFO)
        console_handler.setFormatter(
            logging.Formatter("%(asctime)s [%(levelname)s] %(name)s: %(message)s", "%H:%M:%S")
        )
        logging.getLogger().addHandler(console_handler)

        # Dump recent log history so user sees what happened before
        log_dir = os.path.join(
            os.environ.get("LOCALAPPDATA", os.path.join(os.path.expanduser("~"), "AppData", "Local")),
            "MuraveyChina", "Logs",
        )
        log_file = os.path.join(log_dir, "muravey_china.log")
        if os.path.isfile(log_file):
            try:
                with open(log_file, "r", encoding="utf-8", errors="replace") as f:
                    lines = f.readlines()
                # Show last 50 lines
                for line in lines[-50:]:
                    sys.stdout.write(line)
                sys.stdout.write("\n--- Live log (new messages appear below) ---\n\n")
                sys.stdout.flush()
            except Exception:
                pass

        TrayIcon._console_attached = True

    def _handle_buy(self, icon, item) -> None:
        """Open Whop purchase page in browser."""
        import webbrowser
        webbrowser.open("https://whop.com/checkout/plan_YFqvqKJproMsR")

    def _handle_activate(self, icon, item) -> None:
        """Show activation dialog."""
        if self._on_activate:
            threading.Thread(target=self._on_activate, daemon=True).start()

    def _handle_terminal(self, icon, item) -> None:
        """Open cmd in the Muravey app folder."""
        import subprocess, os
        app_dir = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
        subprocess.Popen(["cmd", "/k", f"cd /d \"{app_dir}\" && title Muravey Terminal"], creationflags=0x10)

    def _handle_debug(self, icon, item) -> None:
        """Toggle debug mode."""
        if self._on_debug_toggle:
            threading.Thread(target=self._on_debug_toggle, daemon=True).start()

    def _handle_quit(self, icon, item) -> None:
        self.stop()
        self._on_quit()

    # ------------------------------------------------------------------ #
    # Busy animation
    # ------------------------------------------------------------------ #

    def _start_animation(self) -> None:
        if self._anim_thread and self._anim_thread.is_alive():
            return
        self._anim_stop.clear()
        self._anim_thread = threading.Thread(target=self._animate, daemon=True, name="tray-anim")
        self._anim_thread.start()

    def _stop_animation(self) -> None:
        self._anim_stop.set()
        if self._icon:
            self._icon.icon = _load_icon(getattr(self, "_current_theme", "dark"))

    def _animate(self) -> None:
        """Cycle through opacity levels to indicate busy state."""
        import time

        from PIL import Image, ImageDraw

        step = 0
        while not self._anim_stop.is_set():
            opacity = 128 + int(127 * abs((step % 20) / 10 - 1))
            img = Image.new("RGBA", (64, 64), (0, 0, 0, 0))
            draw = ImageDraw.Draw(img)
            draw.ellipse([8, 8, 56, 56], fill=(50, 120, 220, opacity))
            if self._icon:
                self._icon.icon = img
            step += 1
            time.sleep(0.1)
