"""nikame test — First-class testing suite for NIKAME projects.

Automates venv creation, dependency installation, and pytest execution
with support for unit, integration, and e2e levels.
"""

from __future__ import annotations

import asyncio
import os
import shutil
import subprocess
import sys
from pathlib import Path

import click

from nikame.utils.logger import console


@click.command()
@click.option("--unit", is_flag=True, help="Run unit tests only.")
@click.option("--integration", is_flag=True, help="Run integration tests.")
@click.option("--e2e", is_flag=True, help="Run end-to-end tests.")
@click.option("--coverage", is_flag=True, help="Run with coverage reporting.")
@click.option("--watch", is_flag=True, help="Watch for changes and re-run.")
@click.option(
    "--project-dir",
    type=click.Path(exists=True, path_type=Path),
    default=Path("."),
    help="Project directory to test.",
)
def test(
    unit: bool,
    integration: bool,
    e2e: bool,
    coverage: bool,
    watch: bool,
    project_dir: Path,
) -> None:
    """Run tests for the current NIKAME project."""
    import asyncio
    asyncio.run(execute_test_async(
        unit=unit,
        integration=integration,
        e2e=e2e,
        coverage=coverage,
        watch=watch,
        project_dir=project_dir
    ))

async def execute_test_async(
    unit: bool,
    integration: bool,
    e2e: bool,
    coverage: bool,
    watch: bool,
    project_dir: Path,
) -> None:
    """Async implementation of nikame test."""
    console.print("[info]🚀 Starting NIKAME Test Suite...[/info]")

    if not (project_dir / "app").exists():
        console.print("[error]✗ Error: 'app/' directory not found.[/error]")
        raise SystemExit(1)

    venv_dir = project_dir / ".venv"
    if not venv_dir.exists():
        console.print("[info]Creating isolated .venv...[/info]")
        await asyncio.create_subprocess_exec(sys.executable, "-m", "venv", str(venv_dir))

    if os.name == "nt":
        pip_bin = venv_dir / "Scripts" / "pip.exe"
        pytest_bin = venv_dir / "Scripts" / "pytest.exe"
    else:
        pip_bin = venv_dir / "bin" / "pip"
        pytest_bin = venv_dir / "bin" / "pytest"

    with console.status("[info]Checking dependencies...[/info]"):
        req_files = ["requirements.txt", "requirements-test.txt"]
        for req_file in req_files:
            req_path = project_dir / req_file
            if req_path.exists():
                proc = await asyncio.create_subprocess_exec(str(pip_bin), "install", "-r", str(req_path), stdout=subprocess.DEVNULL)
                await proc.wait()

    # Database Setup
    env = os.environ.copy()
    pg_user = env.get("POSTGRES_USER", "postgres")
    pg_pw = env.get("POSTGRES_PASSWORD", "postgres")
    pg_host = env.get("POSTGRES_HOST", "localhost")
    pg_port = env.get("POSTGRES_PORT", "5432")
    
    test_db_url_alembic = f"postgresql://{pg_user}:{pg_pw}@{pg_host}:{pg_port}/app_test"
    test_db_url_app = f"postgresql+asyncpg://{pg_user}:{pg_pw}@{pg_host}:{pg_port}/app_test"
    
    env["TEST_DATABASE_URL"] = test_db_url_app
    env["TEST_REDIS_URL"] = "redis://localhost:6379/1"
    
    if (project_dir / "alembic.ini").exists():
        alembic_bin = venv_dir / ("Scripts" if os.name == "nt" else "bin") / "alembic"
        console.print("[info]Syncing Test Database...[/info]")
        _create_test_db(pg_user, pg_pw, pg_host, pg_port, "app_test")
        
        env_alembic = env.copy()
        env_alembic["DATABASE_URL"] = test_db_url_alembic
        await asyncio.create_subprocess_exec(str(alembic_bin), "upgrade", "head", env=env_alembic)

    # Prepare Pytest
    cmd = [str(pytest_bin), "--color=yes"]
    if unit: cmd.extend(["-m", "unit"])
    if integration: cmd.extend(["-m", "integration"])
    if e2e: cmd.extend(["-m", "e2e"])
    if coverage: cmd.extend(["--cov=app"])

    from rich.live import Live
    from rich.panel import Panel
    from rich.table import Table

    counts = {"passed": 0, "failed": 0, "error": 0, "skipped": 0}
    logs = []

    def make_summary():
        table = Table(title="Test Summary", box=None)
        table.add_column("Result", style="bold")
        table.add_column("Count", justify="right")
        
        table.add_row("[green]Passed[/green]", str(counts["passed"]))
        table.add_row("[red]Failed[/red]", str(counts["failed"]))
        table.add_row("[bold red]Errors[/bold red]", str(counts["error"]))
        table.add_row("[yellow]Skipped[/yellow]", str(counts["skipped"]))
        
        return Panel(table, title="[bold]Test Results[/bold]", border_style="blue")

    console.print("\n[bold cyan]─── Running Pytest ───[/bold cyan]\n")
    
    process = await asyncio.create_subprocess_exec(
        *cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )

    with Live(make_summary(), refresh_per_second=4) as live:
        while True:
            line = await process.stdout.readline()
            if not line: break
            text = line.decode().strip()
            if not text: continue
            
            # Simple parsing for summary counts
            if " PASSED " in text: counts["passed"] += 1
            if " FAILED " in text: counts["failed"] += 1
            if " ERROR " in text: counts["error"] += 1
            if " SKIPPED " in text: counts["skipped"] += 1
            
            logs.append(text)
            if len(logs) > 10: logs.pop(0)
            
            # We show logs in a separate panel if we wanted, but let's just update summary for now
            live.update(make_summary())
            # Actually, let's show logs too
            combined = Table.grid(expand=True)
            combined.add_row(make_summary())
            combined.add_row(Panel("\n".join(logs), title="[dim]Streaming Logs[/dim]", border_style="dim"))
            live.update(combined)

    await process.wait()
    if process.returncode != 0 and counts["failed"] == 0 and counts["error"] == 0:
        # Some other failure (collect failure etc)
        console.print("[bold red]✗ Pytest failed to start or crashed.[/bold red]")
    elif process.returncode == 0:
        console.print("\n[bold green]✅ All tests passed successfully![/bold green]")
    else:
        console.print(f"\n[bold red]❌ Tests failed with {counts['failed']} failures.[/bold red]")

def _create_test_db(user, pw, host, port, db_name) -> None:
    # Same as before, but keeping it for completeness in this file
    container_names = ["postgres"]
    try:
        result = subprocess.run(["docker", "ps", "--filter", "ancestor=postgres", "--format", "{{.Names}}"], capture_output=True, text=True)
        if result.stdout: container_names.extend(result.stdout.strip().split("\n"))
    except Exception: pass

    for container in container_names:
        try:
            res = subprocess.run(["docker", "exec", container, "psql", "-U", user, "-lqt"], capture_output=True, text=True)
            if db_name in res.stdout: return
            subprocess.run(["docker", "exec", container, "psql", "-U", user, "-c", f"CREATE DATABASE {db_name};"], check=True, capture_output=True)
            console.print(f"[success]✓ Created test database: {db_name}[/success]")
            return
        except Exception: continue
    
    if shutil.which("psql"):
        os.environ["PGPASSWORD"] = pw
        try: subprocess.run(["psql", "-h", host, "-p", port, "-U", user, "-c", f"CREATE DATABASE {db_name};"], capture_output=True)
        except Exception: pass
