"""
koci Run Controller

Executes pipelines locally using container runtime.
"""

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

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


class Run(Controller):
    """Controller for the 'run' command."""

    class Meta:
        label = 'run'
        stacked_on = 'base'
        stacked_type = 'nested'
        description = 'Execute a koci pipeline locally'
        help = 'Run pipeline locally using Docker/Podman'

        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}),

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

            (['--branch'],
             {'help': 'Simulate branch name for conditions',
              'action': 'store',
              'dest': 'branch',
              'default': None}),

            (['--tag'],
             {'help': 'Simulate tag name for conditions',
              'action': 'store',
              'dest': 'tag',
              'default': None}),

            (['-e', '--env'],
             {'help': 'Set environment variable (KEY=VALUE)',
              'action': 'append',
              'dest': 'env',
              'default': []}),

            (['-w', '--workspace'],
             {'help': 'Workspace directory to mount',
              'action': 'store',
              'dest': 'workspace',
              'default': None}),

            (['--no-color'],
             {'help': 'Disable colored output',
              'action': 'store_true',
              'dest': 'no_color',
              'default': False}),

            (['--dry-run'],
             {'help': 'Parse and validate without executing',
              'action': 'store_true',
              'dest': 'dry_run',
              'default': False}),

            (['-p', '--parallel'],
             {'help': 'Max parallel jobs (default: 4, use 1 for sequential)',
              'action': 'store',
              'dest': 'parallel',
              'type': int,
              'default': 4}),
        ]

    @ex(help='Execute pipeline')
    def _default(self):
        """Execute the pipeline."""
        # Determine verbosity from app debug setting or -v flags
        verbosity = Verbosity.NORMAL
        if self.app.debug:
            verbosity = Verbosity.DEBUG

        # Setup formatter
        formatter = OutputFormatter(
            verbosity=verbosity,
            use_color=not self.app.pargs.no_color,
        )

        # Get workspace path
        workspace = self.app.pargs.workspace or os.getcwd()

        # Load secrets from .koci.secrets.yml if it exists
        env = self._load_secrets(workspace)

        # Parse command-line environment variables (override secrets)
        for env_str in self.app.pargs.env or []:
            if '=' in env_str:
                key, value = env_str.split('=', 1)
                env[key] = value

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

        # Build execution context
        detected_tag = self.app.pargs.tag or self._get_git_tag()
        context = ExecutionContext(
            branch=self.app.pargs.branch or self._get_git_branch(),
            tag=detected_tag,
            event='manual',
            env=env,
        )

        # Show detected context (always show, not just debug)
        if detected_tag:
            formatter.info(f"Detected git tag: {detected_tag}")
        if env:
            # Show secrets (masked) that were loaded
            secret_keys = [k for k in env.keys() if 'PAT' in k or 'TOKEN' in k or 'SECRET' in k or 'KEY' in k]
            if secret_keys:
                formatter.info(f"Loaded secrets: {', '.join(secret_keys)}")

        # Dry run - just validate
        if self.app.pargs.dry_run:
            formatter.success(f"Pipeline '{pipeline.name}' is valid")
            formatter.print_pipeline_graph(pipeline)
            return

        # Run pipeline - use live display in normal mode, verbose in debug mode
        if self.app.debug:
            # Debug mode: verbose streaming output
            formatter.pipeline_start(
                pipeline.name,
                len(pipeline.stages),
                len(pipeline.get_all_jobs()),
            )

            try:
                engine = PipelineEngine(
                    workspace_path=workspace,
                    output_callback=lambda msg: formatter.print(msg, level=Verbosity.VERBOSE),
                    max_parallel=self.app.pargs.parallel,
                )

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

                # Print summary
                formatter.print_summary(result)
                formatter.pipeline_end(result.success, result.duration_seconds)

                if not result.success:
                    self.app.exit_code = 1

            except Exception as e:
                formatter.error(f"Pipeline execution failed: {e}")
                import traceback
                traceback.print_exc()
                self.app.exit_code = 1
        else:
            # Normal mode: live display with spinners
            self._run_with_live_display(pipeline, workspace, context, formatter)

    def _run_with_live_display(self, pipeline, workspace, context, formatter):
        """Run pipeline with live display showing spinners and checkmarks."""
        # Create and populate live display
        display = LivePipelineDisplay(pipeline.name)

        for stage in pipeline.stages:
            jobs = []
            for job in stage.jobs:
                # Expand matrix jobs
                expanded = job.get_expanded_jobs()
                for exp_job in expanded:
                    jobs.append({
                        'name': exp_job.name,
                        'image': exp_job.image,
                        'steps': [{'name': s.name} for s in exp_job.steps],
                    })
            display.add_stage(stage.name, jobs)

        try:
            display.start()

            # Create engine with live display as event handler
            engine = PipelineEngine(
                workspace_path=workspace,
                output_callback=lambda msg: None,  # Suppress verbose output
                event_handler=display,
                max_parallel=self.app.pargs.parallel,
            )

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

        except Exception as e:
            display.stop()
            formatter.error(f"Pipeline execution failed: {e}")
            self.app.exit_code = 1
            return

        finally:
            display.stop()

        # Print summary
        display.print_summary(result)

        if not result.success:
            self.app.exit_code = 1

    def _load_secrets(self, workspace: str) -> dict:
        """Load secrets from .koci.secrets.yml file."""
        secrets = {}
        secrets_file = Path(workspace) / '.koci.secrets.yml'

        if secrets_file.exists():
            try:
                import yaml
                with open(secrets_file, 'r') as f:
                    data = yaml.safe_load(f)
                    if isinstance(data, dict):
                        # Convert all values to strings
                        for key, value in data.items():
                            if value is not None:
                                secrets[str(key)] = str(value)
            except Exception as e:
                # Log warning but continue
                if self.app.debug:
                    print(f"Warning: Failed to load secrets file: {e}")

        return secrets

    def _get_git_branch(self) -> 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=self.app.pargs.workspace or os.getcwd(),
            )
            if result.returncode == 0:
                return result.stdout.strip()
        except Exception:
            pass
        return 'main'

    def _get_git_tag(self) -> str:
        """Try to get current git tag (if HEAD is tagged)."""
        try:
            import subprocess
            # Check if HEAD is exactly at a tag
            result = subprocess.run(
                ['git', 'describe', '--tags', '--exact-match', 'HEAD'],
                capture_output=True,
                text=True,
                cwd=self.app.pargs.workspace or os.getcwd(),
            )
            if result.returncode == 0:
                return result.stdout.strip()
        except Exception:
            pass
        return None
