# System
import datetime as dt
import pathlib as pl
import typing as ty

# Third Party
import numpy as np
import pydantic as pc
import tomlkit as tk

# Internal
from .orientation import AngleUnits, IndexOrder


class RotationStep(pc.BaseModel):
    """Single rotation step.

    Both angle and axis are stored in the current convention/units.
    - Angle: stored in units specified by parent metadata.angle_units
    - Axis: stored in convention specified by parent metadata.index_order
    """

    axis: ty.Literal["X", "Y", "Z"]
    angle: float = 0.0
    visible: bool = True
    name: str = ""
    name_editable: bool = True
    deletable: bool = True


class RotationMetadata(pc.BaseModel):
    """Metadata for TOML files."""

    coordinate_system: ty.Literal["LPS"] = "LPS"
    index_order: IndexOrder = IndexOrder.ITK
    angle_units: AngleUnits = AngleUnits.RADIANS
    timestamp: str = pc.Field(default_factory=lambda: dt.datetime.now().isoformat())
    volume_label: str = ""
    deletable: bool = True


class RotationSequence(pc.BaseModel):
    """Complete rotation sequence.

    All data (angle, axis, and origin) are stored in the current convention/units:
    - Angle: stored in units specified by metadata.angle_units
    - Axis: stored in convention specified by metadata.index_order
    - Origin: stored in axis order specified by metadata.index_order

    When convention/units change in the UI, all existing data is converted.
    """

    model_config = pc.ConfigDict(frozen=False)

    metadata: RotationMetadata = pc.Field(default_factory=RotationMetadata)
    angles_list: list[RotationStep] = pc.Field(default_factory=list)
    mpr_origin: list[float] = pc.Field(
        default_factory=lambda: [0.0, 0.0, 0.0],
        description="MPR origin position [x, y, z] in current index_order convention",
    )

    @pc.field_validator("mpr_origin")
    @classmethod
    def validate_mpr_origin(cls, v):
        """Ensure mpr_origin is a 3-element list of floats."""
        if not isinstance(v, list) or len(v) != 3:
            raise ValueError("mpr_origin must be a 3-element list [x, y, z]")
        return [float(x) for x in v]

    def to_toml(self) -> str:
        """Serialize to TOML using stored serialization preferences."""
        from . import __version__

        data = self.model_dump(mode="json")
        toml_str = tk.dumps(data)

        version_comment = f"# Generated by cardio version {__version__}\n\n"
        return version_comment + toml_str

    @classmethod
    def from_toml(cls, toml_content: str) -> "RotationSequence":
        """Deserialize from TOML (no conversions - loads as-is)."""
        doc = tk.loads(toml_content)
        data = dict(doc)
        return cls(**data)

    @classmethod
    def from_file(cls, path: pl.Path) -> "RotationSequence":
        """Load from TOML file."""
        with open(path, "r") as f:
            return cls.from_toml(f.read())

    def to_file(self, path: pl.Path):
        """Save to TOML file using stored serialization preferences."""
        path.parent.mkdir(parents=True, exist_ok=True)
        with open(path, "w") as f:
            f.write(self.to_toml())
