"""Command line interface for the BPM Finder package."""

from __future__ import annotations

import argparse
from typing import Sequence

from .audio import analyze_audio_file
from .conversion import bpm_to_ms, ms_to_bpm
from .ranges import normalize_bpm, tempo_bucket
from .tap_tempo import _bpm_from_intervals


def _parse_csv_numbers(value: str) -> list[float]:
    parts = [item.strip() for item in value.split(",") if item.strip()]
    if not parts:
        raise argparse.ArgumentTypeError("Please provide at least one numeric value.")

    try:
        return [float(item) for item in parts]
    except ValueError as exc:
        raise argparse.ArgumentTypeError("All values must be valid numbers.") from exc


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="bpm-finder",
        description="BPM Finder: lightweight tempo helpers for Python workflows.",
    )
    subparsers = parser.add_subparsers(dest="command", required=True)

    analyze_audio_parser = subparsers.add_parser("analyze-audio", help="Estimate BPM from a WAV audio file.")
    analyze_audio_parser.add_argument("file", help="Path to a local uncompressed WAV file.")
    analyze_audio_parser.add_argument("--min-bpm", default=70.0, type=float, help="Minimum target BPM.")
    analyze_audio_parser.add_argument("--max-bpm", default=180.0, type=float, help="Maximum target BPM.")

    tap_parser = subparsers.add_parser("tap", help="Estimate BPM from tap intervals.")
    tap_parser.add_argument(
        "--intervals",
        required=True,
        type=_parse_csv_numbers,
        help="Comma-separated tap intervals in milliseconds.",
    )
    tap_parser.add_argument(
        "--strategy",
        default="median",
        choices=["median", "mean"],
        help="Representative interval strategy.",
    )

    bpm_to_ms_parser = subparsers.add_parser("bpm-to-ms", help="Convert BPM to milliseconds.")
    bpm_to_ms_parser.add_argument("--bpm", required=True, type=float, help="BPM value.")
    bpm_to_ms_parser.add_argument(
        "--division",
        required=True,
        type=int,
        help="Note division denominator: 4=quarter, 8=eighth, 1=whole.",
    )

    ms_to_bpm_parser = subparsers.add_parser("ms-to-bpm", help="Convert milliseconds to BPM.")
    ms_to_bpm_parser.add_argument("--ms", required=True, type=float, help="Milliseconds value.")
    ms_to_bpm_parser.add_argument(
        "--division",
        required=True,
        type=int,
        help="Note division denominator: 4=quarter, 8=eighth, 1=whole.",
    )

    normalize_parser = subparsers.add_parser("normalize", help="Normalize BPM into a practical range.")
    normalize_parser.add_argument("--bpm", required=True, type=float, help="BPM value.")
    normalize_parser.add_argument("--min-bpm", default=70.0, type=float, help="Minimum target BPM.")
    normalize_parser.add_argument("--max-bpm", default=180.0, type=float, help="Maximum target BPM.")

    bucket_parser = subparsers.add_parser("bucket", help="Return a simple tempo bucket.")
    bucket_parser.add_argument("--bpm", required=True, type=float, help="BPM value.")

    return parser


def main(argv: Sequence[str] | None = None) -> int:
    parser = build_parser()
    args = parser.parse_args(argv)

    if args.command == "analyze-audio":
        print(analyze_audio_file(args.file, min_bpm=args.min_bpm, max_bpm=args.max_bpm))
        return 0

    if args.command == "tap":
        print(_bpm_from_intervals(args.intervals, strategy=args.strategy))
        return 0

    if args.command == "bpm-to-ms":
        print(bpm_to_ms(args.bpm, note_division=args.division))
        return 0

    if args.command == "ms-to-bpm":
        print(ms_to_bpm(args.ms, note_division=args.division))
        return 0

    if args.command == "normalize":
        print(normalize_bpm(args.bpm, min_bpm=args.min_bpm, max_bpm=args.max_bpm))
        return 0

    if args.command == "bucket":
        print(tempo_bucket(args.bpm))
        return 0

    parser.error("Unknown command.")
    return 2
