"""CLI principal do tecjustica-ocr."""

from __future__ import annotations

import contextlib
import io
import os
import sys
from pathlib import Path
from typing import Optional

import typer
from rich.console import Console

from . import __version__

app = typer.Typer(
    name="tecjustica-ocr",
    help="Extração OCR de processos judiciais — PDF para Markdown.",
    add_completion=False,
    invoke_without_command=True,
)
console = Console()


def version_callback(value: bool):
    if value:
        console.print(f"tecjustica-ocr {__version__}")
        raise typer.Exit()


@app.callback()
def callback(
    version: Optional[bool] = typer.Option(
        None,
        "--version",
        "-V",
        callback=version_callback,
        is_eager=True,
        help="Mostra a versão.",
    ),
):
    """Extração OCR de processos judiciais — PDF para Markdown."""


@contextlib.contextmanager
def suppress_paddle_logs():
    """Suprime as mensagens coloridas do PaddleOCR/PaddleX (fd-level)."""
    import logging

    for name in ["paddle", "paddlex", "paddleocr", "ppocr"]:
        logging.getLogger(name).setLevel(logging.ERROR)

    # Redireciona no nível do OS para capturar prints C/nativos
    devnull = os.open(os.devnull, os.O_WRONLY)
    old_stdout_fd = os.dup(1)
    old_stderr_fd = os.dup(2)
    os.dup2(devnull, 1)
    os.dup2(devnull, 2)
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    sys.stdout = io.StringIO()
    sys.stderr = io.StringIO()
    try:
        yield
    finally:
        sys.stdout = old_stdout
        sys.stderr = old_stderr
        os.dup2(old_stdout_fd, 1)
        os.dup2(old_stderr_fd, 2)
        os.close(old_stdout_fd)
        os.close(old_stderr_fd)
        os.close(devnull)


@app.command()
def run(
    caminho: Path = typer.Argument(
        ...,
        help="Arquivo PDF ou pasta com PDFs.",
        exists=True,
    ),
    output: Path = typer.Option(
        Path("output"),
        "--output",
        "-o",
        help="Diretório de saída.",
    ),
    model: str = typer.Option(
        "mobile",
        "--model",
        "-m",
        help="Modelo: mobile (rápido) ou server (qualidade).",
    ),
    device: str = typer.Option(
        "auto",
        "--device",
        "-d",
        help="Dispositivo: auto, gpu ou cpu.",
    ),
    scale: int = typer.Option(
        2,
        "--scale",
        "-s",
        help="Escala de render: 1, 2 ou 3.",
    ),
    workers: Optional[int] = typer.Option(
        None,
        "--workers",
        "-w",
        help="Workers para render paralelo (default: auto).",
    ),
    mode: str = typer.Option(
        "text",
        "--mode",
        help="Modo: text (PP-OCRv5) ou structure (PP-StructureV3).",
    ),
    min_score: float = typer.Option(
        0.5,
        "--min-score",
        help="Score mínimo para filtrar textos.",
    ),
    verbose: bool = typer.Option(
        False,
        "--verbose",
        "-v",
        help="Output detalhado.",
    ),
):
    """Extrai texto de PDFs judiciais e gera Markdown."""
    from .config import OcrConfig

    config_kwargs = dict(
        model=model,
        device=device,
        scale=scale,
        mode=mode,
        min_score=min_score,
        output_dir=output,
        verbose=verbose,
    )
    if workers is not None:
        config_kwargs["workers"] = workers

    try:
        config = OcrConfig(**config_kwargs)
    except ValueError as e:
        console.print(f"[red]Erro de configuração:[/red] {e}")
        raise typer.Exit(1)

    # Descobrir PDFs
    if caminho.is_file():
        pdf_files = [caminho]
    elif caminho.is_dir():
        pdf_files = sorted(caminho.glob("*.pdf"))
        if not pdf_files:
            console.print(f"[yellow]Nenhum PDF encontrado em {caminho}[/yellow]")
            raise typer.Exit(1)
    else:
        console.print(f"[red]{caminho} não é um arquivo ou diretório válido.[/red]")
        raise typer.Exit(1)

    # Verificar dependências antes de prosseguir
    try:
        from .device import resolve_device

        resolved_device = resolve_device(config.device)
    except Exception:
        console.print(
            "[red]PaddlePaddle não encontrado.[/red]\n"
            "Execute primeiro: [bold]tecjustica-ocr init[/bold]"
        )
        raise typer.Exit(1)

    console.print(f"[bold]tecjustica-ocr {__version__}[/bold]")
    console.print(f"  Dispositivo: {resolved_device}")
    console.print(f"  Modelo: {config.model} | Modo: {config.mode} | Escala: {config.scale}")
    console.print(f"  PDFs: {len(pdf_files)} | Output: {config.output_dir}")
    console.print()

    # Carregar motor OCR uma vez (reutilizado entre PDFs)
    ocr_engine = None
    structure_engine = None

    with console.status("[bold cyan]Carregando modelo OCR...") as status:
        try:
            from .engine import create_ocr, create_structure, process_pdf
            from .writer import write_markdown

            with suppress_paddle_logs():
                if config.mode == "structure":
                    structure_engine = create_structure(config)
                else:
                    ocr_engine = create_ocr(config)
        except ImportError as e:
            console.print(
                f"[red]Dependência não encontrada:[/red] {e}\n"
                "Execute primeiro: [bold]tecjustica-ocr init[/bold]"
            )
            raise typer.Exit(1)

    console.print("[green]Modelo carregado.[/green]\n")

    from .progress import OcrProgress, print_summary

    total_pages = 0
    total_texts = 0
    total_elapsed = 0.0

    for i, pdf_path in enumerate(pdf_files):
        label = f"[{i + 1}/{len(pdf_files)}]" if len(pdf_files) > 1 else ""
        console.print(f"[bold cyan]Processando:{label}[/bold cyan] {pdf_path.name}")

        with OcrProgress(verbose=config.verbose) as prog:
            from .renderer import get_page_count

            num_pages = get_page_count(pdf_path)
            prog.add_render_task(num_pages)
            prog.add_ocr_task(num_pages)

            result = process_pdf(
                pdf_path,
                config,
                on_render_progress=prog.update_render,
                on_ocr_progress=prog.update_ocr,
                ocr_engine=ocr_engine,
                structure_engine=structure_engine,
            )

            md_path = write_markdown(result, config.output_dir, mode=config.mode)
            elapsed = prog.elapsed

        total_pages += result.num_pages
        total_texts += result.total_texts
        total_elapsed += elapsed

        console.print(
            f"  [green]OK[/green] {result.num_pages} págs, "
            f"{result.total_texts} textos, {elapsed:.1f}s → {md_path}"
        )

    print_summary(len(pdf_files), total_pages, total_texts, total_elapsed)


@app.command()
def init(
    download: bool = typer.Option(
        True,
        "--download/--no-download",
        help="Baixar modelos automaticamente.",
    ),
    model: str = typer.Option(
        "all",
        "--model",
        "-m",
        help="Modelo para baixar: mobile, server ou all.",
    ),
    install: bool = typer.Option(
        True,
        "--install/--no-install",
        help="Instalar dependências faltantes automaticamente.",
    ),
):
    """Verifica o sistema, instala dependências e baixa modelos OCR."""
    from rich.panel import Panel
    from rich.table import Table

    from .doctor import (
        PADDLE_CPU_PKG,
        PADDLE_GPU_INDEX,
        PADDLE_GPU_PKG,
        download_models,
        has_nvidia_gpu,
        install_paddle,
        install_paddleocr,
        install_pypdfium2,
        run_all_checks,
    )

    console.print()
    console.print(
        Panel(
            f"[bold]tecjustica-ocr {__version__}[/bold] — Configuração do ambiente",
            border_style="blue",
        )
    )

    # ── Fase 1: Diagnóstico ─────────────────────────────────────────────
    console.print("\n[bold]1. Verificando sistema...[/bold]\n")
    checks = run_all_checks()

    table = Table(show_header=True, border_style="dim")
    table.add_column("Verificação", style="bold")
    table.add_column("Status", justify="center")
    table.add_column("Detalhe")

    required_failures = []
    warnings = []
    fixable = []

    for check in checks:
        if check.ok:
            status = "[green]OK[/green]"
        elif check.required:
            status = "[red]FALHA[/red]"
            required_failures.append(check)
        else:
            status = "[yellow]AVISO[/yellow]"
            warnings.append(check)
        if not check.ok and check.auto_fixable:
            fixable.append(check)
        table.add_row(check.name, status, check.detail)

    console.print(table)

    # Parar se há falha não-corrigível (ex: Python < 3.10)
    non_fixable_failures = [f for f in required_failures if not f.auto_fixable]
    if non_fixable_failures:
        console.print(
            f"\n[red bold]Problemas que precisam de correção manual ({len(non_fixable_failures)}):[/red bold]"
        )
        for f in non_fixable_failures:
            console.print(f"  [red]•[/red] {f.name}: {f.detail}")
        raise typer.Exit(1)

    if warnings:
        console.print(f"\n[yellow]Avisos ({len(warnings)}):[/yellow]")
        for w in warnings:
            console.print(f"  [yellow]•[/yellow] {w.name}: {w.detail}")

    # ── Fase 2: Instalar dependências faltantes ─────────────────────────
    did_install = False
    if fixable and install:
        console.print("\n[bold]2. Instalando dependências...[/bold]\n")
        did_install = True

        fixable_names = {f.name for f in fixable}

        # PaddlePaddle
        if "PaddlePaddle" in fixable_names:
            gpu_available = has_nvidia_gpu()
            variant = "GPU" if gpu_available else "CPU"
            console.print(f"  Instalando PaddlePaddle ({variant})...")
            console.print(f"  [dim](isso pode levar alguns minutos)[/dim]")
            if install_paddle(use_gpu=gpu_available):
                console.print(f"  [green]OK[/green] PaddlePaddle {variant} instalado")
            else:
                console.print(f"  [red]ERRO[/red] Falha ao instalar PaddlePaddle {variant}")
                if gpu_available:
                    console.print(
                        f"  Tente manualmente:\n    pip install {PADDLE_GPU_PKG} -i {PADDLE_GPU_INDEX}"
                    )
                else:
                    console.print(f"  Tente manualmente:\n    pip install {PADDLE_CPU_PKG}")
                raise typer.Exit(1)

        # PaddleOCR
        if "PaddleOCR" in fixable_names:
            console.print("  Instalando PaddleOCR...")
            if install_paddleocr():
                console.print("  [green]OK[/green] PaddleOCR instalado")
            else:
                console.print("  [red]ERRO[/red] Falha ao instalar PaddleOCR")
                console.print("  Tente manualmente: pip install 'paddleocr[all]'")
                raise typer.Exit(1)

        # pypdfium2
        if "pypdfium2" in fixable_names:
            console.print("  Instalando pypdfium2...")
            if install_pypdfium2():
                console.print("  [green]OK[/green] pypdfium2 instalado")
            else:
                console.print("  [red]ERRO[/red] Falha ao instalar pypdfium2")
                raise typer.Exit(1)

        # Re-verificar após instalação
        console.print("\n  Verificando instalação...")
        checks = run_all_checks()
        still_broken = [c for c in checks if not c.ok and c.required]
        if still_broken:
            console.print("\n[red]Ainda há problemas após instalação:[/red]")
            for f in still_broken:
                console.print(f"  [red]•[/red] {f.name}: {f.detail}")
            raise typer.Exit(1)
        console.print("  [green]OK[/green] Todas as dependências instaladas\n")

    elif fixable and not install:
        console.print("\n[yellow]Dependências faltantes (use --install para instalar):[/yellow]")
        for f in fixable:
            console.print(f"  [yellow]•[/yellow] {f.name}: {f.detail}")
        raise typer.Exit(1)

    # ── Fase 3: Download de modelos ─────────────────────────────────────
    step = "3" if did_install else "2"
    if download:
        console.print(f"\n[bold]{step}. Baixando modelos OCR...[/bold]")
        console.print(f"  [dim](primeira vez pode levar alguns minutos — ~500 MB)[/dim]\n")

        if model == "all":
            models_to_dl = ["mobile", "server"]
        else:
            models_to_dl = [model]

        from .device import resolve_device

        device = resolve_device("auto")

        for m in models_to_dl:
            with console.status(f"  [cyan]Baixando modelo {m}...[/cyan]"):
                try:
                    with suppress_paddle_logs():
                        download_models([m], device=device)
                    console.print(f"  [green]OK[/green] Modelo {m} pronto")
                except RuntimeError as e:
                    console.print(f"  [red]ERRO[/red] {e}")
                    raise typer.Exit(1)

    # ── Resumo final ────────────────────────────────────────────────────
    from .device import resolve_device

    device = resolve_device("auto")
    gpu_label = "[green]GPU[/green]" if "gpu" in device else "[yellow]CPU (mais lento)[/yellow]"

    console.print(
        Panel(
            f"[green bold]Tudo pronto![/green bold]\n\n"
            f"  Dispositivo: {gpu_label}\n"
            f"  Comando: [bold]tecjustica-ocr run <pdf>[/bold]\n\n"
            f"  Dica: use [bold]-m mobile[/bold] para velocidade\n"
            f"        use [bold]-m server[/bold] para qualidade",
            title="Pronto para usar",
            border_style="green",
        )
    )
