"""Run commands for iseq-flow CLI."""

import asyncio
import time

import click

from iseq_flow.api import APIError, ComputeAPIClient


@click.group()
def runs():
    """Manage pipeline runs."""
    pass


@runs.command("list")
@click.option("-p", "--project", required=True, help="Project ID")
@click.option("--limit", default=20, help="Maximum runs to display")
def list_runs(project: str, limit: int):
    """List runs for a project."""

    async def _list():
        client = ComputeAPIClient()
        try:
            runs_list = await client.list_runs(project, limit=limit)

            if not runs_list:
                click.echo("No runs found.")
                return

            # Print header
            click.echo(f"{'ID':<40} {'NAME':<35} {'STATUS':<12}")
            click.echo("-" * 90)

            for r in runs_list:
                click.echo(f"{r.id:<40} {r.name:<35} {r.status:<12}")

        except APIError as e:
            click.echo(f"Error: {e}", err=True)
            raise SystemExit(1)

    asyncio.run(_list())


@runs.command("submit")
@click.option("-p", "--project", required=True, help="Project ID")
@click.option("--pipeline", required=True, help="Pipeline slug (e.g., nextflow-minimal)")
@click.option("--param", "-P", multiple=True, help="Parameter in KEY=VALUE format")
@click.option("--tag", "-t", multiple=True, help="Tag for the run")
@click.option("--profile", help="Override Nextflow profile")
@click.option("--watch", is_flag=True, help="Watch run status after submission")
def submit_run(
    project: str,
    pipeline: str,
    param: tuple[str, ...],
    tag: tuple[str, ...],
    profile: str | None,
    watch: bool,
):
    """Submit a new pipeline run.

    \b
    Examples:
      flow runs submit -p PROJECT --pipeline nextflow-minimal \\
        -P nf_profile_name=client_config_test \\
        -P analysis_name=my_analysis \\
        -P sample_id=sample1 \\
        -P fastq_tumor=gs://bucket/R1.fastq.gz \\
        -P fastq_tumor=gs://bucket/R2.fastq.gz

      flow runs submit -p PROJECT --pipeline nextflow-minimal \\
        -P analysis_name=test --watch
    """

    async def _submit():
        client = ComputeAPIClient()
        try:
            # Get pipeline ID from slug
            pipeline_obj = await client.get_pipeline(pipeline)
            pipeline_id = pipeline_obj.id

            # Parse parameters - handle multiple values for same key
            params = {}
            for p in param:
                if "=" not in p:
                    click.echo(f"Invalid parameter format: {p} (use KEY=VALUE)", err=True)
                    raise SystemExit(1)
                key, value = p.split("=", 1)
                if key in params:
                    # Multiple values for same key - convert to list
                    if isinstance(params[key], list):
                        params[key].append(value)
                    else:
                        params[key] = [params[key], value]
                else:
                    params[key] = value

            # Submit run
            run = await client.submit_run(
                project_id=project,
                pipeline_id=pipeline_id,
                params=params,
                tags=list(tag),
                profile=profile,
            )

            click.echo("Run submitted successfully!")
            click.echo(f"  ID: {run.id}")
            click.echo(f"  Name: {run.name}")
            click.echo(f"  Status: {run.status}")

            if watch:
                click.echo("\nWatching run status (Ctrl+C to stop)...")
                await _watch_run(client, run.id)

        except APIError as e:
            click.echo(f"Error: {e}", err=True)
            raise SystemExit(1)

    asyncio.run(_submit())


@runs.command("status")
@click.argument("run_id")
def run_status(run_id: str):
    """Get status of a run.

    RUN_ID is the run identifier.
    """

    async def _status():
        client = ComputeAPIClient()
        try:
            run = await client.get_run(run_id)

            click.echo(f"Run: {run.name}")
            click.echo(f"  ID: {run.id}")
            click.echo(f"  Status: {run.status}")
            click.echo(f"  Pipeline ID: {run.pipeline_id}")
            click.echo(f"  Profile: {run.profile or 'default'}")

            if run.created_at:
                click.echo(f"  Created: {run.created_at}")
            if run.started_at:
                click.echo(f"  Started: {run.started_at}")
            if run.finished_at:
                click.echo(f"  Finished: {run.finished_at}")

            if run.output_path:
                click.echo(f"  Output: {run.output_path}")
            if run.error_message:
                click.echo(f"  Error: {run.error_message}")

            if run.params:
                click.echo("  Parameters:")
                for k, v in run.params.items():
                    click.echo(f"    {k}: {v}")

            if run.tags:
                click.echo(f"  Tags: {', '.join(run.tags)}")

        except APIError as e:
            click.echo(f"Error: {e}", err=True)
            raise SystemExit(1)

    asyncio.run(_status())


@runs.command("cancel")
@click.argument("run_id")
@click.confirmation_option(prompt="Are you sure you want to cancel this run?")
def cancel_run(run_id: str):
    """Cancel a running or queued run.

    RUN_ID is the run identifier.
    """

    async def _cancel():
        client = ComputeAPIClient()
        try:
            await client.cancel_run(run_id)
            click.echo(f"Run {run_id} cancelled.")
        except APIError as e:
            click.echo(f"Error: {e}", err=True)
            raise SystemExit(1)

    asyncio.run(_cancel())


@runs.command("watch")
@click.argument("run_id")
@click.option("--interval", default=10, help="Polling interval in seconds")
def watch_run(run_id: str, interval: int):
    """Watch run status until completion.

    RUN_ID is the run identifier.
    """

    async def _watch():
        client = ComputeAPIClient()
        try:
            await _watch_run(client, run_id, interval)
        except APIError as e:
            click.echo(f"Error: {e}", err=True)
            raise SystemExit(1)

    asyncio.run(_watch())


async def _watch_run(client: ComputeAPIClient, run_id: str, interval: int = 10):
    """Watch run status until terminal state."""
    terminal_statuses = {"succeeded", "failed", "cancelled"}
    last_status = None

    try:
        while True:
            run = await client.get_run(run_id)

            if run.status != last_status:
                timestamp = time.strftime("%H:%M:%S")
                click.echo(f"[{timestamp}] Status: {run.status}")
                last_status = run.status

                if run.status == "running" and run.started_at:
                    click.echo(f"           Started: {run.started_at}")

            if run.status in terminal_statuses:
                click.echo()
                if run.status == "succeeded":
                    click.echo("Run completed successfully!")
                    if run.output_path:
                        click.echo(f"Output: {run.output_path}")
                elif run.status == "failed":
                    click.echo(f"Run failed: {run.error_message or 'Unknown error'}")
                else:
                    click.echo("Run was cancelled.")
                break

            await asyncio.sleep(interval)

    except KeyboardInterrupt:
        click.echo("\nStopped watching (run continues in background).")
