from pathlib import Path

import typer
from rich.console import Console
from typer.core import TyperGroup

from c2m_by_nery import __version__
from c2m_by_nery.core import (
    ConfigError,
    ConfigManager,
    run_conversion,
    run_single_file_conversion,
)
from c2m_by_nery.presentation import print_error_log, print_summary_table


class C2mGroup(TyperGroup):
    def resolve_command(self, ctx, args):
        if args and args[0] not in self.commands:
            args = ["convert", args[0], *args[1:]]
        return super().resolve_command(ctx, args)


app = typer.Typer(
    name="c2m",
    help="Convert code files to Markdown.",
    add_completion=False,
    invoke_without_command=True,
    cls=C2mGroup,
)


def version_callback(value: bool) -> None:
    if value:
        typer.echo(f"c2m-by-nery {__version__}")
        raise typer.Exit()


@app.callback(invoke_without_command=True)
def c2m(
    ctx: typer.Context,
    version: bool = typer.Option(
        None,
        "--version",
        "-v",
        help="Show version and exit.",
        callback=version_callback,
        is_eager=True,
    ),
) -> None:
    if ctx.invoked_subcommand is not None:
        return
    ctx.invoke(convert, path=Path("."))


@app.command("convert", help="Convert path to Markdown (default when no command is given).")
def convert(
    ctx: typer.Context,
    path: Path = typer.Argument(
        ".",
        help="Path to a directory (repository root) or a single file (default: current directory).",
        path_type=Path,
    ),
    tokens: int | None = typer.Option(
        None,
        "--tokens",
        "-t",
        help="Override the default token limit per output file.",
    ),
    quiet: bool = typer.Option(
        False,
        "--quiet",
        "-q",
        help="Suppress progress and summary output.",
    ),
    verbose: bool = typer.Option(
        False,
        "--verbose",
        help="Show detailed progress and full summary table.",
    ),
    zip_output: bool = typer.Option(
        False,
        "--zip",
        "-z",
        help="Bundle output into a .zip file (works for any output format).",
    ),
    flat: bool = typer.Option(
        False,
        "--flat",
        "-f",
        help="Output all .md files in a single folder (no subfolders).",
    ),
    single_file: bool = typer.Option(
        False,
        "--single-file",
        "-s",
        help="Merge all content into one output.md file.",
    ),
    frontmatter: bool = typer.Option(
        True,
        "--frontmatter/--no-frontmatter",
        help="Toggle YAML metadata inclusion.",
    ),
) -> None:
    root_path = path.resolve()

    if root_path.is_file():
        try:
            config = ConfigManager.load(root_path.parent)
        except ConfigError as e:
            console = Console(stderr=True)
            console.print(f"[red]Configuration error:[/red] {e}")
            raise typer.Exit(1) from e
        token_limit = tokens if tokens is not None else config.token_limit
        console = Console(force_terminal=not quiet)
        report = run_single_file_conversion(
            root_path,
            config,
            token_limit,
            console=console,
            verbose=verbose,
            quiet=quiet,
            zip_output=zip_output,
            flat_output=flat,
            single_file=single_file,
            frontmatter=frontmatter,
        )
    else:
        if not root_path.exists():
            console = Console(stderr=True)
            console.print(f"[red]Path does not exist:[/red] {root_path}")
            raise typer.Exit(1)
        if not root_path.is_dir():
            console = Console(stderr=True)
            console.print(f"[red]Path is not a directory or file:[/red] {root_path}")
            raise typer.Exit(1)
        try:
            config = ConfigManager.load(root_path)
        except ConfigError as e:
            console = Console(stderr=True)
            console.print(f"[red]Configuration error:[/red] {e}")
            raise typer.Exit(1) from e
        token_limit = tokens if tokens is not None else config.token_limit
        console = Console(force_terminal=not quiet)
        report = run_conversion(
            root_path,
            config,
            token_limit,
            console=console,
            verbose=verbose,
            quiet=quiet,
            zip_output=zip_output,
            flat_output=flat,
            single_file=single_file,
            frontmatter=frontmatter,
        )

    if quiet:
        if zip_output and "zip_path" in report:
            console.print(report["zip_path"])
        elif single_file:
            console.print(f"{config.output_path}/{report['timestamp']}/output.md")
        else:
            console.print(f"{config.output_path}/{report['timestamp']}/")
        return

    console.print()
    print_summary_table(console, report)

    console.print()
    console.print(
        f"[green]Done![/green] Processed [bold]{report['files_processed']}[/bold] files, "
        f"wrote [bold]{report['segments_created']}[/bold] segments, "
        f"[bold]{report['total_tokens']}[/bold] total tokens in "
        f"[bold]{report['elapsed_seconds']}[/bold]s."
    )
    if zip_output and "zip_path" in report:
        console.print(f"[dim]Output:[/dim] {report['zip_path']}")
    elif single_file:
        console.print(f"[dim]Output:[/dim] {config.output_path}/{report['timestamp']}/output.md")
    else:
        console.print(f"[dim]Output:[/dim] {config.output_path}/{report['timestamp']}/")

    print_error_log(console, report)


@app.command()
def serve(
    port: int = typer.Option(8000, "--port", "-p", help="Port to bind."),
) -> None:
    import uvicorn

    typer.echo(f"Starting API server at http://0.0.0.0:{port}")
    typer.echo(f"Swagger UI: http://0.0.0.0:{port}/docs")
    uvicorn.run(
        "c2m_by_nery.api.app:app",
        host="0.0.0.0",
        port=port,
        reload=False,
    )


if __name__ == "__main__":
    app()
