"""appxen ingest — batch-upload files to the knowledge base."""

from __future__ import annotations

import time
from pathlib import Path

import typer
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskID

from appxen_cli.display import console, error, success, warn

SUPPORTED_EXTENSIONS = {
    ".pdf", ".docx", ".html", ".htm", ".md", ".csv", ".json", ".txt",
    ".xml", ".yaml", ".yml",
    ".py", ".js", ".ts", ".jsx", ".tsx", ".css", ".sql", ".sh",
    ".rs", ".go", ".java", ".c", ".cpp", ".h", ".rb", ".php",
    ".png", ".jpg", ".jpeg", ".tiff", ".bmp",
}


def _collect_files(
    path: Path,
    glob_pattern: str,
    recursive: bool,
) -> list[Path]:
    """Collect files matching pattern and supported extensions."""
    if path.is_file():
        if path.suffix.lower() in SUPPORTED_EXTENSIONS:
            return [path]
        warn(f"Unsupported file type: {path.suffix}")
        return []

    if not path.is_dir():
        error(f"Path does not exist: {path}")
        raise typer.Exit(1)

    pattern = f"**/{glob_pattern}" if recursive else glob_pattern
    files = sorted(
        f for f in path.glob(pattern)
        if f.is_file() and f.suffix.lower() in SUPPORTED_EXTENSIONS
    )
    return files


def _poll_source(client, source_id: str, timeout: int = 300) -> str:
    """Poll source status until ready/failed or timeout (exponential backoff)."""
    start = time.monotonic()
    delay = 1.0  # start at 1s, grow to max 10s
    while time.monotonic() - start < timeout:
        try:
            data = client.get_source(source_id)
            status = data.get("status", "unknown")
            if status in ("ready", "failed"):
                return status
        except Exception:
            pass
        time.sleep(delay)
        delay = min(delay * 1.5, 10.0)
    return "timeout"


def ingest(
    path: Path = typer.Argument(..., help="File or directory to ingest"),
    glob_pattern: str = typer.Option("*", "--glob", "-g", help="Glob pattern for directory"),
    recursive: bool = typer.Option(True, "--recursive/--no-recursive", help="Recurse into subdirectories"),
    poll: bool = typer.Option(True, "--poll/--no-poll", help="Poll until ingestion completes"),
    json_output: bool = typer.Option(False, "--json", help="Output raw JSON"),
) -> None:
    """Ingest files into the knowledge base."""
    from appxen_cli.main import get_client
    from appxen_cli.display import print_json

    try:
        client = get_client()
    except typer.Exit:
        raise

    files = _collect_files(path, glob_pattern, recursive)
    if not files:
        error("No supported files found.")
        raise typer.Exit(1)

    console.print(f"Found [bold]{len(files)}[/bold] file(s) to ingest.")

    results: list[dict] = []
    failed_count = 0

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        BarColumn(),
        TextColumn("{task.completed}/{task.total}"),
        console=console,
    ) as progress:
        upload_task: TaskID = progress.add_task("Uploading", total=len(files))

        for f in files:
            progress.update(upload_task, description=f"Uploading {f.name}")
            try:
                result = client.upload_file(f)
                results.append({"file": str(f), **result})
            except Exception as e:
                results.append({"file": str(f), "error": str(e)})
                failed_count += 1
            progress.advance(upload_task)

    # Poll for completion
    if poll and not json_output:
        pending = [
            r for r in results
            if r.get("status") in ("queued", "processing") and "source_id" in r
        ]
        if pending:
            console.print(f"Waiting for {len(pending)} file(s) to process...")
            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                BarColumn(),
                TextColumn("{task.completed}/{task.total}"),
                console=console,
            ) as progress:
                poll_task = progress.add_task("Processing", total=len(pending))
                for r in pending:
                    progress.update(poll_task, description=f"Processing {Path(r['file']).name}")
                    final_status = _poll_source(client, r["source_id"])
                    r["status"] = final_status
                    if final_status == "failed":
                        failed_count += 1
                    progress.advance(poll_task)

    if json_output:
        print_json(results)
    else:
        ok = len(results) - failed_count
        console.print()
        success(f"{ok} file(s) ingested successfully.")
        if failed_count:
            error(f"{failed_count} file(s) failed.")

    if failed_count:
        raise typer.Exit(2)
