"""Internal implementation of the wing geometry schema dataclasses."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Literal

from buffalo_wings.airfoil import AirfoilDefinitionSpec

type LengthUnit = Literal["m", "ft"]
type AngleUnit = Literal["deg", "rad"]
type SymmetryMode = Literal["mirror_y", "none"]
type ReferenceAxis = Literal["leading_edge", "quarter_chord", "elastic_axis"]
type JoinContinuity = Literal["C0", "G1"]
type PanelSide = Literal["u_min", "u_max", "v_min", "v_max"]
type BlendMethod = Literal["pointwise", "cst", "camber_thickness"]
type FitMethod = Literal["skin", "least_squares"]
type SpacingMethod = Literal["uniform", "cosine"]


@dataclass(slots=True)
class UnitsSpec:
    """
    Unit declarations used by the serialized schema.

    Attributes
    ----------
    length : {"m", "ft"}, default="m"
        Length unit used for dimensional wing quantities.
    angle : {"deg", "rad"}, default="deg"
        Angle unit used for twist and other angular quantities.
    """

    length: LengthUnit = "m"
    angle: AngleUnit = "deg"


@dataclass(slots=True)
class FrameSpec:
    """
    Human-readable axis labels for the wing coordinate frame.

    Attributes
    ----------
    x : str, default="forward"
        Human-readable label for the positive x-axis.
    y : str, default="right"
        Human-readable label for the positive y-axis.
    z : str, default="up"
        Human-readable label for the positive z-axis.
    """

    x: str = "forward"
    y: str = "right"
    z: str = "up"


@dataclass(slots=True)
class PiecewiseLinearDistribution:
    """
    Piecewise-linear scalar distribution over ``eta``.

    Attributes
    ----------
    type : Literal["piecewise_linear"], default="piecewise_linear"
        Schema discriminator for the distribution type.
    data : list[tuple[float, float]], optional
        Ordered ``(eta, value)`` control points used for linear interpolation.
    """

    type: Literal["piecewise_linear"] = "piecewise_linear"
    data: list[tuple[float, float]] = field(default_factory=list)


type DistributionSpec = PiecewiseLinearDistribution


@dataclass(slots=True)
class RefLineSpec:
    """
    Reference-axis position distributions used for sweep and dihedral.

    Attributes
    ----------
    x_ref : DistributionSpec
        Spanwise distribution of the reference-axis x-position.
    z_ref : DistributionSpec
        Spanwise distribution of the reference-axis z-position.
    """

    x_ref: DistributionSpec
    z_ref: DistributionSpec


@dataclass(slots=True)
class SingleAirfoilRef:
    """
    Reference to a single named airfoil.

    Attributes
    ----------
    type : Literal["single"], default="single"
        Schema discriminator for a single airfoil reference.
    name : str, default=""
        Name of the airfoil in ``WingSpec.airfoils``.
    """

    type: Literal["single"] = "single"
    name: str = ""


@dataclass(slots=True)
class BlendAirfoilRef:
    """
    Spanwise airfoil blend definition for a panel.

    Attributes
    ----------
    type : Literal["blend"], default="blend"
        Schema discriminator for a blended airfoil reference.
    method : {"pointwise", "cst", "camber_thickness"}, default="cst"
        Blend strategy used between the listed stations.
    stations : list[tuple[float, str]], optional
        Ordered ``(eta_local, airfoil_name)`` blend stations within the panel.
    """

    type: Literal["blend"] = "blend"
    method: BlendMethod = "cst"
    stations: list[tuple[float, str]] = field(default_factory=list)


type AirfoilRefSpec = SingleAirfoilRef | BlendAirfoilRef


@dataclass(slots=True)
class DegreesSpec:
    """
    Requested NURBS polynomial degrees.

    Attributes
    ----------
    u : int, default=4
        Polynomial degree in the chordwise surface direction.
    v : int, default=3
        Polynomial degree in the spanwise surface direction.
    """

    u: int = 4
    v: int = 3


@dataclass(slots=True)
class ControlPointSpec:
    """
    Requested NURBS control-point counts.

    Attributes
    ----------
    u : int, default=16
        Number of control points in the chordwise surface direction.
    v : int, default=10
        Number of control points in the spanwise surface direction.
    """

    u: int = 16
    v: int = 10


@dataclass(slots=True)
class FitSpec:
    """
    Surface fitting method for internal NURBS construction.

    Attributes
    ----------
    method : {"skin", "least_squares"}, default="skin"
        Surface fitting strategy used to build the panel NURBS patch.
    """

    method: FitMethod = "skin"


@dataclass(slots=True)
class NurbsSpec:
    """
    Panel-local NURBS construction controls.

    Attributes
    ----------
    degrees : DegreesSpec, optional
        Requested polynomial degrees for the panel patch.
    ctrlpts : ControlPointSpec, optional
        Requested control-point counts for the panel patch.
    fit : FitSpec, optional
        Fitting strategy used during patch construction.
    """

    degrees: DegreesSpec = field(default_factory=DegreesSpec)
    ctrlpts: ControlPointSpec = field(default_factory=ControlPointSpec)
    fit: FitSpec = field(default_factory=FitSpec)


@dataclass(slots=True)
class VlmMeshSpec:
    """
    VLM sampling controls.

    Attributes
    ----------
    n_span : int
        Number of spanwise samples used for the exported VLM mesh.
    n_chord : int
        Number of chordwise samples used for the exported VLM mesh.
    span_spacing : {"uniform", "cosine"}, default="cosine"
        Spanwise spacing rule for mesh sampling.
    chord_spacing : {"uniform", "cosine"}, default="cosine"
        Chordwise spacing rule for mesh sampling.
    """

    n_span: int
    n_chord: int
    span_spacing: SpacingMethod = "cosine"
    chord_spacing: SpacingMethod = "cosine"


@dataclass(slots=True)
class CfdMeshSpec:
    """
    CFD surface sampling controls.

    Attributes
    ----------
    n_span : int
        Number of spanwise samples used for the exported CFD surface mesh.
    n_airfoil : int
        Number of chordwise airfoil samples per section.
    """

    n_span: int
    n_airfoil: int


@dataclass(slots=True)
class MeshSpec:
    """
    Optional export sampling settings.

    Attributes
    ----------
    vlm : VlmMeshSpec or None, optional
        VLM mesh export settings.
    cfd : CfdMeshSpec or None, optional
        CFD surface mesh export settings.
    """

    vlm: VlmMeshSpec | None = None
    cfd: CfdMeshSpec | None = None


@dataclass(slots=True)
class PanelSpec:
    """
    Parametric definition of a single spanwise wing panel.

    Attributes
    ----------
    id : str
        Unique panel identifier within the wing definition.
    eta_range : tuple[float, float]
        Inclusive spanwise parameter range covered by the panel.
    ref_line : RefLineSpec
        Reference-axis position distributions for the panel.
    chord : DistributionSpec
        Spanwise chord distribution over ``eta_range``.
    twist : DistributionSpec
        Spanwise twist distribution over ``eta_range``.
    airfoil : AirfoilRefSpec
        Airfoil definition or blend applied across the panel.
    nurbs : NurbsSpec or None, optional
        Optional panel-local NURBS construction overrides.
    mesh : MeshSpec or None, optional
        Optional export mesh sampling overrides for the panel.
    """

    id: str
    eta_range: tuple[float, float]
    ref_line: RefLineSpec
    chord: DistributionSpec
    twist: DistributionSpec
    airfoil: AirfoilRefSpec
    nurbs: NurbsSpec | None = None
    mesh: MeshSpec | None = None


@dataclass(slots=True)
class JoinSideSpec:
    """
    One side of a panel-to-panel join constraint.

    Attributes
    ----------
    panel : str
        Identifier of the panel participating in the join.
    side : {"u_min", "u_max", "v_min", "v_max"}
        Parametric side of the panel constrained by the join.
    """

    panel: str
    side: PanelSide


@dataclass(slots=True)
class JoinSpec:
    """
    Continuity constraint across a panel interface.

    Attributes
    ----------
    id : str
        Unique identifier for the join definition.
    left : JoinSideSpec
        Left-hand side of the join relation.
    right : JoinSideSpec
        Right-hand side of the join relation.
    continuity : {"C0", "G1"}
        Continuity target enforced across the interface.
    weight : float or None, optional
        Optional relative weighting for the join constraint.
    tangent_scale : float or {"auto"}, default="auto"
        Tangent scaling factor used for directional continuity constraints.
    """

    id: str
    left: JoinSideSpec
    right: JoinSideSpec
    continuity: JoinContinuity
    weight: float | None = None
    tangent_scale: float | Literal["auto"] = "auto"


@dataclass(slots=True)
class WingDefinitionSpec:
    """
    The ``wing`` object from the documented schema.

    Attributes
    ----------
    symmetry : {"mirror_y", "none"}
        Symmetry mode applied to the wing definition.
    half_span : float
        Half-span of the wing in the declared length units.
    reference_axis : {"leading_edge", "quarter_chord", "elastic_axis"}
        Reference axis used for panel placement.
    twist_axis : {"leading_edge", "quarter_chord", "elastic_axis"}
        Axis about which twist values are applied.
    panels : list[PanelSpec]
        Ordered spanwise panel definitions for the wing.
    name : str or None, optional
        Optional human-readable name for the wing definition.
    frame : FrameSpec or None, optional
        Optional axis-label metadata for documentation.
    joins : list[JoinSpec], optional
        Join constraints applied between neighboring panels.
    """

    symmetry: SymmetryMode
    half_span: float
    reference_axis: ReferenceAxis
    twist_axis: ReferenceAxis
    panels: list[PanelSpec]
    name: str | None = None
    frame: FrameSpec | None = None
    joins: list[JoinSpec] = field(default_factory=list)


@dataclass(slots=True)
class WingSpec:
    """
    Top-level wing geometry schema.

    Attributes
    ----------
    schema_version : int
        Version number of the serialized schema format.
    units : UnitsSpec
        Unit declarations for dimensional and angular values.
    wing : WingDefinitionSpec
        Wing geometry definition.
    airfoils : dict[str, AirfoilDefinitionSpec], optional
        Named airfoil library referenced by the wing panels.
    """

    schema_version: int
    units: UnitsSpec
    wing: WingDefinitionSpec
    airfoils: dict[str, AirfoilDefinitionSpec] = field(default_factory=dict)


__all__ = [
    "AirfoilRefSpec",
    "BlendAirfoilRef",
    "BlendMethod",
    "CfdMeshSpec",
    "ControlPointSpec",
    "DegreesSpec",
    "DistributionSpec",
    "FitMethod",
    "FitSpec",
    "FrameSpec",
    "JoinContinuity",
    "JoinSideSpec",
    "JoinSpec",
    "MeshSpec",
    "NurbsSpec",
    "PanelSide",
    "PanelSpec",
    "PiecewiseLinearDistribution",
    "RefLineSpec",
    "ReferenceAxis",
    "SingleAirfoilRef",
    "SpacingMethod",
    "SymmetryMode",
    "UnitsSpec",
    "VlmMeshSpec",
    "WingDefinitionSpec",
    "WingSpec",
]
