"""Command-line interface for pace2fit."""

from __future__ import annotations

from enum import Enum
from pathlib import Path

import typer

from pace2fit.encoder import encode_to_file
from pace2fit.parser import ParseError, parse

app = typer.Typer(
    name="pace2fit",
    help="Generate Garmin FIT workout files from a simple DSL.",
    add_completion=False,
    no_args_is_help=True,
    invoke_without_command=True,
)


class PaceUnit(str, Enum):
    km = "km"
    mi = "mi"


def _summarize_workout(workout) -> str:  # noqa: ANN001
    """Return a short human-readable summary of the parsed workout."""
    from pace2fit.model import RepeatStep, WorkoutStep, PaceTarget, ZoneTarget
    from pace2fit.zones import speed_to_pace

    lines: list[str] = []
    for i, step in enumerate(workout.steps, 1):
        if isinstance(step, RepeatStep):
            inner_parts: list[str] = []
            for s in step.steps:
                inner_parts.append(_describe_single(s))
            lines.append(f"  {i}. {step.count}x ({' + '.join(inner_parts)})")
        elif isinstance(step, WorkoutStep):
            lines.append(f"  {i}. {_describe_single(step)}")
    return "\n".join(lines)


def _describe_single(step) -> str:  # noqa: ANN001
    """Describe a single WorkoutStep."""
    from pace2fit.model import DurationType, PaceTarget, ZoneTarget
    from pace2fit.zones import speed_to_pace

    parts: list[str] = []

    # Intensity prefix
    if step.intensity.value not in ("active",):
        parts.append(step.intensity.value.capitalize())

    # Duration
    if step.duration_type == DurationType.TIME and step.duration_value is not None:
        secs = step.duration_value
        if secs >= 60:
            mins = secs / 60
            if mins == int(mins):
                parts.append(f"{int(mins)}min")
            else:
                parts.append(f"{mins:.1f}min")
        else:
            parts.append(f"{int(secs)}sec")
    elif (
        step.duration_type == DurationType.DISTANCE and step.duration_value is not None
    ):
        metres = step.duration_value
        if metres >= 1000:
            km = metres / 1000
            if km == int(km):
                parts.append(f"{int(km)}km")
            else:
                parts.append(f"{km:.1f}km")
        else:
            parts.append(f"{int(metres)}m")
    elif step.duration_type == DurationType.OPEN:
        if not parts:  # only add "Open" if no intensity prefix
            parts.append("Open")

    # Target
    target = step.target
    if isinstance(target, ZoneTarget):
        parts.append(f"@ Z{target.zone}")
    elif isinstance(target, PaceTarget):
        lo_m, lo_s = speed_to_pace(target.low)
        hi_m, hi_s = speed_to_pace(target.high)
        parts.append(f"@ {lo_m}:{lo_s:02d}-{hi_m}:{hi_s:02d}/km")

    return " ".join(parts)


@app.callback()
def main(ctx: typer.Context) -> None:
    """Generate Garmin FIT workout files from a simple DSL."""
    if ctx.invoked_subcommand is None:
        typer.echo(ctx.get_help())


@app.command()
def generate(
    workout: str = typer.Argument(
        help="Workout description, e.g. '10min@Z2,3x8min@threshold,cooldown@6:30'",
    ),
    output: Path = typer.Option(
        "workout.fit",
        "-o",
        "--output",
        help="Output .fit file path.",
    ),
    name: str | None = typer.Option(
        None,
        "-n",
        "--name",
        help="Workout name (defaults to the input string).",
    ),
    unit: PaceUnit = typer.Option(
        PaceUnit.km,
        "--unit",
        help="Pace unit for input paces (km or mi).",
    ),
) -> None:
    """Parse a workout string and generate a FIT file."""
    if unit == PaceUnit.mi:
        typer.echo("Warning: mile paces not yet supported, interpreting as km.")

    try:
        parsed = parse(workout, name=name)
    except (ParseError, ValueError) as exc:
        typer.echo(f"Parse error: {exc}", err=True)
        raise typer.Exit(code=1) from exc

    typer.echo(f"Workout: {parsed.name}")
    typer.echo("Steps:")
    typer.echo(_summarize_workout(parsed))
    typer.echo()

    try:
        out_path = encode_to_file(parsed, output)
    except Exception as exc:
        typer.echo(f"Encoding error: {exc}", err=True)
        raise typer.Exit(code=1) from exc

    typer.echo(f"Written to {out_path}")


@app.command()
def serve(
    host: str = typer.Option("127.0.0.1", "--host", help="Bind address."),
    port: int = typer.Option(8000, "--port", "-p", help="Port to listen on."),
) -> None:
    """Start the web interface."""
    import uvicorn

    typer.echo(f"Starting pace2fit web UI at http://{host}:{port}")
    uvicorn.run("pace2fit.web:app", host=host, port=port)
