from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import List, Optional

from .exporter import export_selected_candidates, summarize_candidates, summarize_for_ui
from .parser import analyze_log_file, parse_selection


def _format_candidate_line(index: int, candidate) -> str:
    ext = f" ext=0x{candidate.extended_addr:02X}" if candidate.extended_addr is not None else ""
    return (
        f"[{index}] {candidate.protocol_hint.upper()} "
        f"0x{candidate.request_id:X} -> 0x{candidate.response_id:X}{ext} "
        f"msgs={candidate.message_count} req={candidate.request_count} rsp={candidate.response_count} "
        f"pairs={candidate.matched_pairs} uniqReq={candidate.unique_requests} conf={candidate.confidence}"
    )


def _choose_candidates_interactive(total: int) -> List[int]:
    while True:
        user_input = input("Select conversation(s) to convert (e.g. 1,3 or 1-2 or all): ").strip()
        try:
            result = parse_selection(user_input, total)
        except Exception:
            print("Invalid selection. Try again.")
            continue
        if not result:
            print("No valid selection. Try again.")
            continue
        return result


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(
        prog="epy import",
        description="Generate ECUmulator vehicle/ECU specs from log files",
    )
    parser.add_argument("input", help="Path to input log file")
    parser.add_argument(
        "-o",
        "--output-dir",
        default=".",
        help="Output directory for generated spec files (default: current dir)",
    )
    parser.add_argument(
        "--vehicle-name",
        default="Imported Vehicle",
        help="Vehicle name for generated vehicle spec (.vehicle)",
    )
    parser.add_argument(
        "--select",
        default="",
        help="Conversation selection: e.g. '1', '1,3', '1-2', 'all'",
    )
    parser.add_argument(
        "--list-only",
        action="store_true",
        help="Only list detected conversations; do not generate files",
    )
    parser.add_argument(
        "--candidates-json",
        action="store_true",
        help="Print candidate summary as JSON (for UI integration)",
    )
    parser.add_argument(
        "--non-interactive",
        action="store_true",
        help="Do not prompt; if multiple conversations, default to best-ranked unless --select is set",
    )
    parser.add_argument(
        "--overwrite",
        action="store_true",
        help="Allow overwriting existing output files",
    )
    return parser


def main(argv: Optional[List[str]] = None) -> None:
    parser = build_parser()
    args = parser.parse_args(argv)

    input_path = Path(args.input).expanduser().resolve()
    if not input_path.exists():
        sys.exit(f"Input file not found: {input_path}")

    analysis = analyze_log_file(str(input_path))

    print(f"Source format: {analysis.source_type}")
    print(f"Decoded messages: {len(analysis.messages)}")
    print(f"Detected conversations: {len(analysis.candidates)}")

    if args.candidates_json:
        ui_summary = summarize_for_ui(
            analysis.candidates,
            metadata_vin=analysis.metadata.get("vin"),
            messages=analysis.messages,
        )
        payload = {
            "sourceType": analysis.source_type,
            "metadata": analysis.metadata,
            "ui": ui_summary,
            "candidates": summarize_candidates(analysis.candidates),
        }
        print(json.dumps(payload, indent=2))

    if not analysis.candidates:
        sys.exit("No diagnostic request/response conversations were detected.")

    print("\nConversation candidates:")
    for index, candidate in enumerate(analysis.candidates, start=1):
        print(_format_candidate_line(index, candidate))

    ui_summary = summarize_for_ui(
        analysis.candidates,
        metadata_vin=analysis.metadata.get("vin"),
        messages=analysis.messages,
    )
    header = ui_summary["header"]
    if header.get("tamperSuspected"):
        print(f"Warning: {header.get('tamperWarning')}")

    if args.list_only:
        return

    if args.select:
        selected_indices = parse_selection(args.select, len(analysis.candidates))
        if not selected_indices:
            sys.exit("Selection did not match any candidate.")
    elif len(analysis.candidates) == 1:
        selected_indices = [0]
    elif args.non_interactive:
        selected_indices = [0]
        print("Multiple conversations detected; non-interactive mode selected top-ranked candidate #1.")
    else:
        selected_indices = _choose_candidates_interactive(len(analysis.candidates))

    output_dir = Path(args.output_dir).expanduser().resolve()
    result = export_selected_candidates(
        candidates=analysis.candidates,
        output_dir=str(output_dir),
        vehicle_name=args.vehicle_name,
        base_vin=analysis.metadata.get("vin"),
        selected_indices=selected_indices,
        overwrite=args.overwrite,
    )

    print("\nGenerated files:")
    print(f"- vehicle: {result.vehicle_path}")
    for ecu in result.ecus:
        print(
            f"- ecu: {ecu.path} "
            f"({ecu.protocol.upper()} 0x{ecu.request_id:X}->0x{ecu.response_id:X}, name={ecu.ecu_name})"
        )


if __name__ == "__main__":
    main()
