"""
koci Watch Controller

Watches for file changes and auto-runs pipeline.
"""

import os
import time
from pathlib import Path
from cement import Controller, ex

from ..core import parse_pipeline, ExecutionContext, ParserError, ValidationError
from ..output import OutputFormatter, Verbosity


class Watch(Controller):
    """Controller for the 'watch' command."""

    class Meta:
        label = 'watch'
        stacked_on = 'base'
        stacked_type = 'nested'
        description = 'Watch for changes and auto-run pipeline'
        help = 'File watcher with automatic pipeline execution'

        arguments = [
            (['-f', '--file'],
             {'help': 'Path to koci.yml file',
              'action': 'store',
              'dest': 'file',
              'default': None}),

            (['-s', '--stage'],
             {'help': 'Run only a specific stage',
              'action': 'store',
              'dest': 'stage',
              'default': None}),

            (['-p', '--pattern'],
             {'help': 'File patterns to watch (glob)',
              'action': 'append',
              'dest': 'patterns',
              'default': None}),

            (['-i', '--ignore'],
             {'help': 'Patterns to ignore',
              'action': 'append',
              'dest': 'ignore',
              'default': None}),

            (['-d', '--debounce'],
             {'help': 'Debounce time in seconds',
              'action': 'store',
              'dest': 'debounce',
              'type': float,
              'default': 1.0}),

            (['--validate-only'],
             {'help': 'Only validate, do not run',
              'action': 'store_true',
              'dest': 'validate_only',
              'default': False}),
        ]

    @ex(help='Watch and run')
    def _default(self):
        """Start watching for changes."""
        formatter = OutputFormatter(verbosity=Verbosity.NORMAL)

        try:
            from watchdog.observers import Observer
            from watchdog.events import FileSystemEventHandler
        except ImportError:
            formatter.error("watchdog is not installed. Run: pip install watchdog")
            self.app.exit_code = 1
            return

        workspace = Path.cwd()

        # Default patterns
        patterns = self.app.pargs.patterns or ['*.py', '*.js', '*.ts', '*.go', '*.rs', '*.yml', '*.yaml']
        ignore = self.app.pargs.ignore or ['node_modules', '.git', '__pycache__', '.koci', 'venv', 'env']

        # Validate pipeline exists
        try:
            pipeline = parse_pipeline(
                filepath=self.app.pargs.file,
                directory=str(workspace),
            )
            formatter.success(f"Watching pipeline: {pipeline.name}")
        except (ParserError, ValidationError) as e:
            formatter.error(str(e))
            self.app.exit_code = 1
            return

        formatter.print(f"Patterns: {', '.join(patterns)}")
        formatter.print(f"Ignoring: {', '.join(ignore)}")
        formatter.print(f"Debounce: {self.app.pargs.debounce}s")
        formatter.print("\nWatching for changes... (Ctrl+C to stop)\n")

        # Event handler
        running = False

        class ChangeHandler(FileSystemEventHandler):
            def __init__(handler_self):
                handler_self.last_event = 0

            def should_ignore(handler_self, path: str) -> bool:
                """Check if path should be ignored."""
                for pattern in ignore:
                    if pattern in path:
                        return True
                return False

            def matches_pattern(handler_self, path: str) -> bool:
                """Check if path matches watch patterns."""
                from fnmatch import fnmatch
                name = os.path.basename(path)
                return any(fnmatch(name, p) for p in patterns)

            def on_modified(handler_self, event):
                nonlocal running

                if event.is_directory:
                    return

                path = event.src_path

                if handler_self.should_ignore(path):
                    return

                if not handler_self.matches_pattern(path):
                    return

                # Debounce
                now = time.time()
                if now - handler_self.last_event < self.app.pargs.debounce:
                    return
                handler_self.last_event = now

                if running:
                    return

                running = True

                try:
                    rel_path = os.path.relpath(path, workspace)
                    formatter.print(f"\n📝 Changed: {rel_path}")
                    formatter.print("-" * 40)

                    if self.app.pargs.validate_only:
                        self._run_validate(formatter, workspace)
                    else:
                        self._run_pipeline(formatter, workspace)

                finally:
                    running = False
                    formatter.print("\nWatching for changes...")

        # Start watching
        handler = ChangeHandler()
        observer = Observer()
        observer.schedule(handler, str(workspace), recursive=True)
        observer.start()

        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            formatter.print("\n\nStopping watcher...")
            observer.stop()

        observer.join()

    def _run_validate(self, formatter, workspace):
        """Run validation only."""
        try:
            pipeline = parse_pipeline(
                filepath=self.app.pargs.file,
                directory=str(workspace),
            )
            formatter.success(f"Pipeline '{pipeline.name}' is valid")
        except (ParserError, ValidationError) as e:
            formatter.error(str(e))

    def _run_pipeline(self, formatter, workspace):
        """Run the pipeline."""
        from ..runtime import PipelineEngine

        try:
            pipeline = parse_pipeline(
                filepath=self.app.pargs.file,
                directory=str(workspace),
            )
        except (ParserError, ValidationError) as e:
            formatter.error(str(e))
            return

        context = ExecutionContext(
            branch=self._get_git_branch(workspace),
            event='watch',
        )

        try:
            engine = PipelineEngine(
                workspace_path=str(workspace),
                output_callback=lambda msg: formatter.print(msg, level=Verbosity.VERBOSE),
            )

            result = engine.run(
                pipeline,
                stage_filter=self.app.pargs.stage,
                context=context,
            )

            if result.success:
                formatter.success(f"Pipeline completed in {result.duration_seconds:.1f}s")
            else:
                formatter.error(f"Pipeline failed after {result.duration_seconds:.1f}s")

        except Exception as e:
            formatter.error(f"Pipeline error: {e}")

    def _get_git_branch(self, workspace) -> str:
        """Try to get current git branch."""
        try:
            import subprocess
            result = subprocess.run(
                ['git', 'rev-parse', '--abbrev-ref', 'HEAD'],
                capture_output=True,
                text=True,
                cwd=workspace,
            )
            if result.returncode == 0:
                return result.stdout.strip()
        except Exception:
            pass
        return 'main'
