from __future__ import annotations

import struct
from pathlib import Path
from typing import Any


def parse_tc_file(path: Path) -> dict[str, Any]:
    data = path.read_bytes()
    offset = 0

    def read_i32() -> int:
        nonlocal offset
        value = struct.unpack_from("<i", data, offset)[0]
        offset += 4
        return value

    def read_f32() -> float:
        nonlocal offset
        value = struct.unpack_from("<f", data, offset)[0]
        offset += 4
        return value

    def read_string() -> str:
        size = read_i32()
        nonlocal offset
        value = data[offset : offset + size].decode("utf-8", errors="replace")
        offset += size
        return value

    unknown = read_i32()
    player_name = read_string()
    track = read_string()
    car_model = read_string()
    track_layout = read_string()
    lap_time_ms = read_i32()
    num_data_points = read_i32()

    samples = []
    for _ in range(num_data_points):
        if offset + 20 > len(data):
            break
        gear = read_i32()
        position = read_f32()
        speed_kmh = read_f32()
        throttle = read_f32()
        brake = read_f32()
        samples.append(
            {
                "gear": gear,
                "position": position,
                "speed_kmh": speed_kmh,
                "throttle": throttle,
                "brake": brake,
            }
        )

    return {
        "unknown": unknown,
        "player_name": player_name,
        "track": track,
        "car_model": car_model,
        "track_layout": track_layout,
        "lap_time_ms": lap_time_ms,
        "num_data_points": num_data_points,
        "samples": samples,
        "source_path": str(path),
    }


def find_matching_tc_file(ctelemetry_root: Path, *, track: str, track_layout: str | None, car_model: str | None) -> Path | None:
    if not ctelemetry_root.exists():
        return None
    track_token = track.lower()
    layout_token = (track_layout or "").lower()
    car_token = (car_model or "").lower()
    candidates = []
    for path in ctelemetry_root.glob("*.tc"):
        name = path.name.lower()
        if track_token not in name:
            continue
        if layout_token and layout_token not in name:
            continue
        if car_token and car_token not in name:
            continue
        candidates.append(path)
    if not candidates:
        return None
    return max(candidates, key=lambda item: item.stat().st_mtime)


def tc_as_lap(path: Path):
    from .analysis import Lap

    parsed = parse_tc_file(path)
    samples = []
    num_points = max(parsed["num_data_points"], 1)
    lap_time_s = parsed["lap_time_ms"] / 1000.0
    for idx, point in enumerate(parsed["samples"]):
        samples.append(
            {
                "captured_at_unix_s": (idx / float(num_points)) * lap_time_s,
                "normalized_car_position": float(point["position"]),
                "distance_traveled_m": float(point["position"]),
                "speed_kmh": float(point["speed_kmh"]),
                "throttle": float(point["throttle"]),
                "brake": float(point["brake"]),
                "gear": int(point["gear"]),
                "steer_angle_deg": 0.0,
                "slip_angle_rad": [0.0, 0.0, 0.0, 0.0],
                "status": "live",
            }
        )
    return Lap(
        lap_number=1,
        lap_time_ms=int(parsed["lap_time_ms"]),
        lap_time_s=lap_time_s,
        lap_length_m=1.0,
        start_distance_m=0.0,
        sector_times_ms=[],
        samples=samples,
    )
