"""IMU adapters for the V1 standard intermediate format.

Accepted inputs
---------------
1. CSV with header:
   t,gx,gy,gz,ax,ay,az
   - ``t`` in seconds on the IMU clock
   - gyro in rad/s
   - accel in m/s^2

2. NPZ with arrays:
   t, gyro, acc
   shapes: (N,), (N,3), (N,3)
"""

from __future__ import annotations

import csv
from pathlib import Path

import numpy as np

from .contracts import ImuSeries


def load_imu_samples(path: Path | str) -> ImuSeries:
    """Load normalized IMU samples from CSV or NPZ."""

    source = Path(path)
    if not source.exists():
        raise FileNotFoundError(source)
    if source.suffix.lower() == ".csv":
        return _load_imu_csv(source)
    if source.suffix.lower() == ".npz":
        return _load_imu_npz(source)
    raise ValueError(f"unsupported IMU format '{source.suffix}' (use .csv or .npz)")


def _load_imu_csv(path: Path) -> ImuSeries:
    required_order = ["t", "gx", "gy", "gz", "ax", "ay", "az"]
    with path.open("r", encoding="utf-8-sig", newline="") as handle:
        header = next(csv.reader(handle), [])
    names = set(header)
    if not set(required_order).issubset(names):
        raise ValueError(f"IMU CSV must contain columns {sorted(required_order)}, got {sorted(names)}")
    usecols = [header.index(name) for name in required_order]
    data = np.loadtxt(path, delimiter=",", skiprows=1, usecols=usecols, ndmin=2)
    t = np.asarray(data[:, 0], dtype=float).reshape(-1)
    gyro = np.asarray(data[:, 1:4], dtype=float)
    acc = np.asarray(data[:, 4:7], dtype=float)
    order = np.argsort(t)
    return ImuSeries(t_s=t[order], gyro_rad_s=gyro[order], acc_m_s2=acc[order])


def _load_imu_npz(path: Path) -> ImuSeries:
    with np.load(path) as payload:
        keys = set(payload.files)
        if not {"t", "gyro", "acc"}.issubset(keys):
            raise ValueError(f"IMU NPZ must contain t, gyro, acc; got {sorted(keys)}")
        t = np.asarray(payload["t"], dtype=float).reshape(-1)
        gyro = np.asarray(payload["gyro"], dtype=float).reshape(-1, 3)
        acc = np.asarray(payload["acc"], dtype=float).reshape(-1, 3)
    order = np.argsort(t)
    return ImuSeries(t_s=t[order], gyro_rad_s=gyro[order], acc_m_s2=acc[order])


def save_imu_csv(path: Path | str, imu: ImuSeries) -> None:
    """Write IMU samples to the standard CSV format."""

    destination = Path(path)
    destination.parent.mkdir(parents=True, exist_ok=True)
    array = np.column_stack([imu.t_s, imu.gyro_rad_s, imu.acc_m_s2])
    header = "t,gx,gy,gz,ax,ay,az"
    np.savetxt(destination, array, delimiter=",", header=header, comments="")
