"""FileWatcherService - File system change detection.

Contract: CNT-TUI-SVC-003
Traceability: UX-001
"""

from __future__ import annotations

import threading
import time
from collections.abc import Callable
from pathlib import Path
from typing import TYPE_CHECKING

from watchdog.events import FileSystemEvent, FileSystemEventHandler
from watchdog.observers import Observer

if TYPE_CHECKING:
    from watchdog.observers.api import BaseObserver


class _ChangeHandler(FileSystemEventHandler):
    """Internal handler for file system events."""

    def __init__(self, callback: Callable[[], None]) -> None:
        super().__init__()
        self._callback = callback

    def on_any_event(self, event: FileSystemEvent) -> None:
        """Handle any file system event."""
        # Ignore directory events
        if event.is_directory:
            return
        self._callback()


class FileWatcherService:
    """Service for watching file system changes.

    Uses watchdog for cross-platform file system monitoring.
    Implements debouncing to prevent excessive updates.
    """

    def __init__(
        self,
        path: Path,
        on_change: Callable[[], None],
        debounce_seconds: float = 0.5,
        patterns: list[str] | None = None,
    ) -> None:
        """Initialize the file watcher.

        Args:
            path: Directory to watch
            on_change: Callback invoked when changes occur
            debounce_seconds: Minimum time between callbacks
            patterns: Optional glob patterns to filter (e.g., ["*.yaml"])
        """
        self._path = path
        self._on_change = on_change
        self._debounce_seconds = debounce_seconds
        self._patterns = patterns or ["*"]

        self._observer: BaseObserver | None = None
        self._last_event_time: float = 0
        self._lock = threading.Lock()
        self._running = False

    def start(self) -> None:
        """Start watching for file changes."""
        if self._running:
            return

        self._observer = Observer()
        handler = _ChangeHandler(self._handle_event)
        self._observer.schedule(handler, str(self._path), recursive=True)
        self._observer.start()
        self._running = True

    def stop(self) -> None:
        """Stop watching for file changes."""
        if not self._running or self._observer is None:
            return

        self._observer.stop()
        self._observer.join(timeout=2.0)
        self._observer = None
        self._running = False

    def _handle_event(self) -> None:
        """Handle a file system event with debouncing."""
        with self._lock:
            now = time.time()
            if now - self._last_event_time >= self._debounce_seconds:
                self._last_event_time = now
                self._on_change()

    @property
    def is_running(self) -> bool:
        """Check if the watcher is running."""
        return self._running

    def __enter__(self) -> FileWatcherService:
        """Context manager entry."""
        self.start()
        return self

    def __exit__(self, *_args: object) -> None:
        """Context manager exit."""
        self.stop()
