"""kalibr init - Scan for bare LLM calls and propose Router wrapping."""

import importlib.resources
import os
import shutil

import requests
import typer
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax

from kalibr.cli.rewriter import (
    apply_change,
    ensure_env_example,
    ensure_kalibr_in_requirements,
    get_proposed_change,
)
from kalibr.cli.scanner import scan_directory

console = Console()


def _provision_credentials(project_dir: str) -> None:
    """Auto-provision credentials using KALIBR_PROVISIONING_TOKEN."""
    token = os.environ.get("KALIBR_PROVISIONING_TOKEN")
    if not token:
        return

    project_name = os.path.basename(os.path.abspath(project_dir))
    console.print("[cyan]Provisioning credentials...[/cyan]")

    try:
        resp = requests.post(
            "https://kalibr-backend.fly.dev/api/provisioning/provision",
            json={
                "provisioning_token": token,
                "name": project_name,
                "source": "kalibr-init",
            },
            timeout=15,
        )
        resp.raise_for_status()
        data = resp.json()

        api_key = data.get("api_key", "")
        tenant_id = data.get("tenant_id", "")

        if api_key and tenant_id:
            env_path = os.path.join(project_dir, ".env")
            lines = []
            if os.path.exists(env_path):
                with open(env_path, encoding="utf-8") as f:
                    lines = f.readlines()

            # Update or append keys
            key_set = {"KALIBR_API_KEY": api_key, "KALIBR_TENANT_ID": tenant_id}
            updated_keys = set()
            new_lines = []
            for line in lines:
                replaced = False
                for key, value in key_set.items():
                    if line.startswith(f"{key}="):
                        new_lines.append(f"{key}={value}\n")
                        updated_keys.add(key)
                        replaced = True
                        break
                if not replaced:
                    new_lines.append(line)

            for key, value in key_set.items():
                if key not in updated_keys:
                    new_lines.append(f"{key}={value}\n")

            with open(env_path, "w", encoding="utf-8") as f:
                f.writelines(new_lines)

            console.print("[green]Credentials provisioned automatically.[/green]")
        else:
            console.print("[yellow]Provisioning response missing credentials.[/yellow]")
    except requests.RequestException as e:
        console.print(f"[red]Provisioning failed: {e}[/red]")


def _check_credentials() -> None:
    """Check for credentials and print guidance if missing."""
    has_api_key = bool(os.environ.get("KALIBR_API_KEY"))
    has_provisioning = bool(os.environ.get("KALIBR_PROVISIONING_TOKEN"))

    if not has_api_key and not has_provisioning:
        console.print()
        console.print(
            "[yellow]No credentials found. "
            "Get your API key at dashboard.kalibr.systems/settings[/yellow]"
        )
        console.print(
            "[yellow]Or set KALIBR_PROVISIONING_TOKEN to provision "
            "credentials automatically.[/yellow]"
        )


def _write_context_files(project_dir: str) -> None:
    """Write CLAUDE.md and .cursorrules into project_dir if they don't already exist."""
    templates_pkg = "kalibr.templates"
    context_files = [
        ("CLAUDE.md", "✓ Created CLAUDE.md (coding agent context)", "→ CLAUDE.md already exists, skipping"),
        (".cursorrules", "✓ Created .cursorrules (Cursor/Windsurf context)", "→ .cursorrules already exists, skipping"),
    ]

    for filename, created_msg, exists_msg in context_files:
        dest = os.path.join(project_dir, filename)
        if os.path.exists(dest):
            console.print(f"[dim]{exists_msg}[/dim]")
        else:
            try:
                # Use importlib.resources to locate bundled template
                ref = importlib.resources.files(templates_pkg).joinpath(filename)
                with importlib.resources.as_file(ref) as src_path:
                    shutil.copy2(src_path, dest)
                console.print(f"[green]{created_msg}[/green]")
            except Exception as e:  # pragma: no cover
                console.print(f"[yellow]Could not write {filename}: {e}[/yellow]")


def init(
    directory: str = typer.Argument(".", help="Directory to scan (default: current directory)"),
) -> None:
    """Scan for bare LLM calls and propose wrapping them with Kalibr Router."""
    project_dir = os.path.abspath(directory)

    if not os.path.isdir(project_dir):
        console.print(f"[red]Directory not found: {project_dir}[/red]")
        raise typer.Exit(1)

    console.print(f"[bold]Scanning {project_dir} for bare LLM calls...[/bold]")
    console.print()

    matches = scan_directory(project_dir)

    if not matches:
        console.print("[yellow]No bare LLM calls found.[/yellow]")
        console.print()
        console.print("If you are instrumenting an agent framework or your own agent execution,")
        console.print("use auto-instrumentation instead:")
        console.print()
        console.print("  [bold]import kalibr[/bold]  # add as first import in your entry point")
        console.print()
        console.print("[dim]This patches OpenAI, Anthropic, and Google automatically.[/dim]")
        console.print("[dim]No Router wrapping needed — every LLM call is traced.[/dim]")
        console.print("[dim]Docs: https://kalibr.systems/docs/quickstart#auto-instrumentation[/dim]")
        console.print()
        _write_context_files(project_dir)
        _check_credentials()
        return

    console.print(f"Found [bold]{len(matches)}[/bold] bare LLM call(s):\n")

    files_modified = set()
    calls_upgraded = 0
    skip_all = False

    for match in matches:
        if skip_all:
            break

        rel_path = os.path.relpath(match.file_path, project_dir)

        # Show file + line number
        console.print(f"[bold cyan]{rel_path}:{match.line_number}[/bold cyan] ({match.pattern_type})")

        # Show current code with context
        current_lines = match.context_before + [match.matched_text] + match.context_after
        current_code = "\n".join(current_lines)
        console.print(Panel(
            Syntax(current_code, "python", theme="monokai", line_numbers=False),
            title="Current",
            border_style="red",
        ))

        # Show proposed replacement
        proposed = get_proposed_change(match)
        console.print(Panel(
            Syntax(proposed, "python", theme="monokai", line_numbers=False),
            title=f'Proposed (goal="{match.inferred_goal}")',
            border_style="green",
        ))

        # Ask for approval
        choice = typer.prompt(
            "Apply this change? [y/n/skip all]",
            default="y",
            show_default=False,
        )
        choice = choice.strip().lower()

        if choice == "skip all":
            skip_all = True
            console.print("[yellow]Skipping remaining changes.[/yellow]")
        elif choice == "y":
            if apply_change(match):
                files_modified.add(match.file_path)
                calls_upgraded += 1
                console.print("[green]Applied.[/green]")
            else:
                console.print("[red]Failed to apply change.[/red]")
        else:
            console.print("[dim]Skipped.[/dim]")

        console.print()

    # Post-apply steps
    if calls_upgraded > 0:
        ensure_kalibr_in_requirements(project_dir)
        ensure_env_example(project_dir)

    # Provision credentials if token is available
    if os.environ.get("KALIBR_PROVISIONING_TOKEN"):
        _provision_credentials(project_dir)

    # Summary
    console.print("[bold]Summary:[/bold]")
    console.print(f"  {len(files_modified)} file(s) modified, {calls_upgraded} LLM call(s) upgraded")

    # Write coding agent context files
    console.print()
    _write_context_files(project_dir)

    _check_credentials()
