"""antiqa run-flow / antiqa flows — execute and manage test flows."""

from __future__ import annotations

from pathlib import Path

import typer
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from antiqa.config.manager import config_exists, load_config
from antiqa.crawler.crawler import DEFAULT_GRAPH_PATH

console = Console()


def run_flow(name: str, headed: bool | None = None) -> None:
    """Execute a named flow against the target application."""
    if not config_exists():
        console.print("[red]No antiqa.json found. Run [bold]antiqa init[/bold] first.[/red]")
        raise typer.Exit(code=1)

    config = load_config()

    if headed is not None:
        config.browser.headless = not headed

    from antiqa.browser.manager import BrowserManager
    from antiqa.flows.parser import FlowParser
    from antiqa.flows.runner import FlowRunner

    parser = FlowParser(config.flows_dir)

    try:
        flow = parser.resolve_with_includes(name)
    except FileNotFoundError:
        console.print(f"[red]Flow '{name}' not found in {config.flows_dir}[/red]")
        raise typer.Exit(code=1)

    console.print(Panel(
        f"[bold cyan]Running flow:[/bold cyan] {flow.name}\n"
        f"[dim]{flow.description}[/dim]\n"
        f"[dim]{len(flow.steps)} steps, {len(flow.assertions)} assertions[/dim]",
        expand=False,
    ))

    mgr = BrowserManager(config.browser, logs_dir=config.logs_dir)
    page = mgr.launch()

    try:
        runner = FlowRunner(page, config=config, screenshots_dir=f"{config.logs_dir}/screenshots")
        result = runner.run(flow)
    finally:
        mgr.close()

    from antiqa.utils.cleanup import remove_screenshots
    remove_screenshots(f"{config.logs_dir}/screenshots")

    # Print results
    console.print()
    step_table = Table(show_header=True, header_style="bold", expand=False)
    step_table.add_column("#", style="dim", width=4)
    step_table.add_column("Action")
    step_table.add_column("Description")
    step_table.add_column("Status")
    step_table.add_column("Time", justify="right")

    for sr in result.steps:
        status = "[green]PASS[/green]" if sr.passed else "[red]FAIL[/red]"
        step_table.add_row(
            str(sr.step_number),
            sr.action,
            sr.description[:60],
            status,
            f"{sr.action_result.duration_ms:.0f}ms",
        )

    console.print(step_table)

    if result.assertions:
        console.print()
        assert_table = Table(title="Assertions", show_header=True, header_style="bold", expand=False)
        assert_table.add_column("Type")
        assert_table.add_column("Expected")
        assert_table.add_column("Actual")
        assert_table.add_column("Status")

        for ar in result.assertions:
            status = "[green]PASS[/green]" if ar.passed else "[red]FAIL[/red]"
            assert_table.add_row(ar.type, ar.expected[:40], ar.actual[:40], status)
        console.print(assert_table)

    console.print()
    summary = result.summary()
    if result.passed:
        console.print(Panel(
            f"[bold green]PASSED[/bold green]  "
            f"Steps: {summary['steps_passed']}/{summary['steps_total']}  "
            f"Assertions: {summary['assertions_passed']}/{summary['assertions_total']}  "
            f"Time: {summary['duration_ms']:.0f}ms",
            expand=False,
        ))
    else:
        console.print(Panel(
            f"[bold red]FAILED[/bold red]  "
            f"Steps: {summary['steps_passed']}/{summary['steps_total']}  "
            f"Assertions: {summary['assertions_passed']}/{summary['assertions_total']}  "
            f"Time: {summary['duration_ms']:.0f}ms\n"
            f"[dim]{summary['error'] or ''}[/dim]",
            expand=False,
        ))
        raise typer.Exit(code=1)


def run_flows_list() -> None:
    """List all available flows."""
    if not config_exists():
        console.print("[red]No antiqa.json found. Run [bold]antiqa init[/bold] first.[/red]")
        raise typer.Exit(code=1)

    config = load_config()

    from antiqa.flows.parser import FlowParser

    parser = FlowParser(config.flows_dir)
    flows = parser.list_flows()

    if not flows:
        console.print(f"[yellow]No flows found in {config.flows_dir}[/yellow]")
        console.print("[dim]Create YAML files in that directory to define test flows.[/dim]")
        return

    table = Table(title="Available Flows", show_header=True, header_style="bold magenta", expand=False)
    table.add_column("Name", style="cyan")
    table.add_column("Description")
    table.add_column("Steps", justify="right")
    table.add_column("Assertions", justify="right")
    table.add_column("Includes")

    for flow in flows:
        table.add_row(
            flow.name,
            flow.description or "[dim]—[/dim]",
            str(len(flow.steps)),
            str(len(flow.assertions)),
            ", ".join(flow.include) if flow.include else "[dim]—[/dim]",
        )

    console.print()
    console.print(table)
    console.print()


def run_generate_flows(overwrite: bool = False, ai: bool = False) -> None:
    """Auto-generate YAML flows from the navigation graph."""
    if not config_exists():
        console.print("[red]No antiqa.json found. Run [bold]antiqa init[/bold] first.[/red]")
        raise typer.Exit(code=1)

    graph_path = Path(DEFAULT_GRAPH_PATH)
    if not graph_path.is_file():
        console.print("[red]No navigation graph found. Run [bold]antiqa crawl[/bold] first.[/red]")
        raise typer.Exit(code=1)

    config = load_config()

    from antiqa.crawler.graph import NavigationGraph

    graph = NavigationGraph.load(graph_path)

    if ai:
        import yaml

        from antiqa.agents.flow_generator import AIFlowGeneratorAgent

        console.print("[cyan]Using AI to generate comprehensive test flows...[/cyan]\n")
        agent = AIFlowGeneratorAgent(config)

        with console.status("[cyan]AI is analyzing pages and generating flows..."):
            flows = agent.generate_all(graph)

        if not flows:
            console.print("[yellow]AI could not generate flows. Falling back to heuristics.[/yellow]")
        else:
            flows_dir = Path(config.flows_dir)
            flows_dir.mkdir(parents=True, exist_ok=True)
            saved: list[Path] = []

            for flow in flows:
                path = flows_dir / f"ai_{flow.name}.yaml"
                if path.exists() and not overwrite:
                    continue
                flow_data = {
                    "name": flow.name,
                    "description": flow.description,
                    "steps": [_flow_step_to_dict(s) for s in flow.steps],
                }
                if flow.assertions:
                    flow_data["assertions"] = [_flow_assertion_to_dict(a) for a in flow.assertions]
                path.write_text(yaml.dump(flow_data, default_flow_style=False, sort_keys=False))
                saved.append(path)

            if saved:
                console.print(Panel(
                    f"[bold green]AI generated {len(saved)} flow(s):[/bold green]\n\n"
                    + "\n".join(f"  [cyan]{p}[/cyan]" for p in saved)
                    + "\n\n[dim]Includes happy paths, negative tests, and edge cases.\n"
                    "Run [bold]antiqa flows list[/bold] to see all flows.[/dim]",
                    expand=False,
                ))
                return
            else:
                console.print("[yellow]All AI flows already exist. Use --overwrite to regenerate.[/yellow]")
                return

    from antiqa.flows.generator import FlowGenerator

    generator = FlowGenerator(graph=graph, app_url=config.app_url, flows_dir=config.flows_dir)
    generated = generator.generate_all(overwrite=overwrite)

    if not generated:
        console.print("[yellow]No new flows to generate (forms already have flows, or no forms found).[/yellow]")
        console.print("[dim]Use --overwrite to regenerate existing flows.[/dim]")
        return

    console.print(Panel(
        f"[bold green]Generated {len(generated)} flow(s):[/bold green]\n\n"
        + "\n".join(f"  [cyan]{p}[/cyan]" for p in generated)
        + "\n\n[dim]These are starter flows — edit them to fit your needs.\n"
        "Run [bold]antiqa flows list[/bold] to see all available flows.[/dim]",
        expand=False,
    ))


def _flow_step_to_dict(step) -> dict:
    d: dict = {"action": step.action}
    if step.url:
        d["url"] = step.url
    if step.selector:
        d["selector"] = step.selector
    if step.value:
        d["value"] = step.value
    if step.key:
        d["key"] = step.key
    if step.name:
        d["name"] = step.name
    if step.timeout_ms:
        d["timeout_ms"] = step.timeout_ms
    if step.direction:
        d["direction"] = step.direction
    if step.description:
        d["description"] = step.description
    return d


def _flow_assertion_to_dict(a) -> dict:
    d: dict = {"type": a.type}
    if a.value:
        d["value"] = a.value
    if a.selector:
        d["selector"] = a.selector
    return d
