"""Panic Button for emergency hide/show toggle and data cleanup.

Two modes:
1. HIDE/SHOW TOGGLE (hotkey: ctrl+shift+f12):
   - First press: instantly hide ALL windows, stop AI requests, make program invisible
   - Second press: re-enable everything, restore windows

2. EMERGENCY CLEANUP (programmatic):
   - MINIMAL: logs only
   - STANDARD: + temp files + caches
   - AGGRESSIVE: + conversations + history + snippets + knowledge base
"""

from __future__ import annotations

import logging
import os
import shutil
import sys
import threading
from collections.abc import Callable
from enum import Enum

log = logging.getLogger(__name__)


class CleanupLevel(Enum):
    """Levels of data cleanup for privacy protection."""

    MINIMAL = "minimal"
    STANDARD = "standard"
    AGGRESSIVE = "aggressive"


class PanicButton:
    """Emergency hide/show toggle and data cleanup.

    Hotkey toggle:
      - First press hides all windows, cancels AI requests, disables other hotkeys.
      - Second press restores everything.

    Also provides emergency_cleanup() / emergency_exit() for full data wipe.
    """

    def __init__(self, data_dir: str | None = None) -> None:
        self.data_dir = data_dir or os.path.expanduser("~/.muravey-china")
        self._hidden = False
        self._lock = threading.Lock()

        # Callbacks set by AppContext after construction
        self._hide_all_cb: Callable[[], None] | None = None
        self._show_all_cb: Callable[[], None] | None = None

        log.info("PanicButton initialized (data_dir=%s)", self.data_dir)

    # ------------------------------------------------------------------ #
    # Hide / show toggle
    # ------------------------------------------------------------------ #

    @property
    def is_hidden(self) -> bool:
        """Return True if the program is currently in panic-hidden state."""
        return self._hidden

    def set_callbacks(
        self,
        hide_all: Callable[[], None],
        show_all: Callable[[], None],
    ) -> None:
        """Register hide/show callbacks from the app context."""
        self._hide_all_cb = hide_all
        self._show_all_cb = show_all

    def toggle(self) -> None:
        """Toggle between hidden and visible state.

        Thread-safe. Called from the hotkey handler.
        """
        with self._lock:
            if self._hidden:
                self._restore()
            else:
                self._hide()

    def _hide(self) -> None:
        """Hide everything — make the program invisible."""
        log.warning("PANIC: hiding all windows and stopping activity")
        self._hidden = True
        if self._hide_all_cb:
            try:
                self._hide_all_cb()
            except Exception:
                log.exception("Error in panic hide callback")

    def _restore(self) -> None:
        """Restore everything — make the program visible again."""
        log.info("PANIC RESTORE: re-enabling all windows and activity")
        self._hidden = False
        if self._show_all_cb:
            try:
                self._show_all_cb()
            except Exception:
                log.exception("Error in panic restore callback")

    # ------------------------------------------------------------------ #
    # Emergency cleanup (existing functionality)
    # ------------------------------------------------------------------ #

    def emergency_cleanup(
        self,
        cleanup_level: CleanupLevel = CleanupLevel.AGGRESSIVE,
    ) -> dict[str, bool]:
        """Execute emergency cleanup and return results per operation.

        Args:
            cleanup_level: Level of cleanup to perform.

        Returns:
            Dict mapping operation name to success status.
        """
        log.warning("Emergency cleanup requested: level=%s", cleanup_level.value)
        results: dict[str, bool] = {}

        # MINIMAL: logs
        results["logs"] = self._delete_dir_contents(os.path.join(self.data_dir, "logs"))

        # STANDARD: + temp + caches
        if cleanup_level in (CleanupLevel.STANDARD, CleanupLevel.AGGRESSIVE):
            results["temp"] = self._delete_dir_contents(os.path.join(self.data_dir, "temp"))
            results["screenshots"] = self._delete_dir_contents(
                os.path.join(self.data_dir, "screenshots")
            )
            results["cache"] = self._delete_file(os.path.join(self.data_dir, "cache.db"))

        # AGGRESSIVE: + conversations + history + snippets + knowledge
        if cleanup_level == CleanupLevel.AGGRESSIVE:
            results["conversations"] = self._delete_dir_contents(
                os.path.join(self.data_dir, "conversations"),
            )
            results["history"] = self._delete_file(os.path.join(self.data_dir, "history.json"))
            results["snippets"] = self._delete_file(os.path.join(self.data_dir, "snippets.json"))
            results["knowledge"] = self._delete_file(os.path.join(self.data_dir, "knowledge.db"))
            results["cost"] = self._delete_file(os.path.join(self.data_dir, "cost_today.json"))

        succeeded = sum(1 for v in results.values() if v)
        log.info(
            "Emergency cleanup done: %d/%d succeeded (level=%s)",
            succeeded,
            len(results),
            cleanup_level.value,
        )
        return results

    def emergency_exit(self, cleanup_level: CleanupLevel = CleanupLevel.AGGRESSIVE) -> None:
        """Execute cleanup and force exit the application."""
        self.emergency_cleanup(cleanup_level)
        log.warning("Emergency exit — terminating process")
        sys.exit(0)

    def _delete_file(self, path: str) -> bool:
        """Securely delete a single file (overwrite with zeros first)."""
        if not os.path.exists(path):
            return True
        try:
            # Overwrite with zeros before deletion — full file, chunked for large files
            size = os.path.getsize(path)
            if size > 0:
                chunk = 10_000_000
                with open(path, "wb") as f:
                    remaining = size
                    while remaining > 0:
                        n = min(remaining, chunk)
                        f.write(b"\x00" * n)
                        remaining -= n
            os.remove(path)
            log.debug("Securely deleted: %s", path)
            return True
        except OSError:
            log.exception("Failed to delete: %s", path)
            return False

    def _delete_dir_contents(self, dir_path: str) -> bool:
        """Delete all contents of a directory (secure)."""
        if not os.path.exists(dir_path):
            return True
        try:
            for entry in os.listdir(dir_path):
                full_path = os.path.join(dir_path, entry)
                if os.path.isfile(full_path):
                    self._delete_file(full_path)
                elif os.path.isdir(full_path):
                    shutil.rmtree(full_path, ignore_errors=True)
            log.debug("Cleared directory: %s", dir_path)
            return True
        except OSError:
            log.exception("Failed to clear directory: %s", dir_path)
            return False
