"""Public factory helpers for schema-driven airfoil construction."""

from __future__ import annotations

from typing import Literal

from .airfoil import Airfoil
from .airfoil_schema import (
    AirfoilDefinitionSpec,
    Naca4AirfoilParamsSpec,
    Naca4AirfoilSpec,
    Naca4ModifiedAirfoilParamsSpec,
    Naca4ModifiedAirfoilSpec,
    Naca5AirfoilParamsSpec,
    Naca5AirfoilSpec,
    Naca5ModifiedAirfoilParamsSpec,
    Naca5ModifiedAirfoilSpec,
)
from .naca4_airfoil import Naca4AirfoilClassic, Naca4AirfoilParams
from .naca4_modified_airfoil import (
    Naca4ModifiedAirfoilClassic,
    Naca4ModifiedAirfoilParams,
)
from .naca5_airfoil import Naca5AirfoilClassic, Naca5AirfoilParams
from .naca5_modified_airfoil import (
    Naca5ModifiedAirfoilClassic,
    Naca5ModifiedAirfoilParams,
)


class AirfoilFactory:
    """
    Construct public airfoil runtime objects from user-facing specs.

    This factory centralizes the mapping from serialized airfoil schema
    objects to the concrete runtime classes used by the geometry library.
    """

    @staticmethod
    def from_spec(spec: AirfoilDefinitionSpec) -> Airfoil:
        """
        Build an airfoil object from a schema definition.

        Parameters
        ----------
        spec : AirfoilDefinitionSpec
            Serialized airfoil definition to convert into a runtime object.

        Returns
        -------
        Airfoil
            Runtime airfoil instance corresponding to ``spec``.

        Raises
        ------
        ValueError
            If a NACA specification does not provide exactly one of
            ``designation`` or ``params``.
        NotImplementedError
            If the airfoil type is not yet supported by the factory.
        """
        if isinstance(spec, Naca4AirfoilSpec):
            if spec.designation is not None and spec.params is None:
                return Naca4AirfoilClassic(spec)
            if spec.designation is None and spec.params is not None:
                return Naca4AirfoilParams(spec)
            raise ValueError(
                "NACA 4-digit specs must provide exactly one of designation "
                "or params."
            )
        if isinstance(spec, Naca4ModifiedAirfoilSpec):
            if spec.designation is not None and spec.params is None:
                return Naca4ModifiedAirfoilClassic(spec)
            if spec.designation is None and spec.params is not None:
                return Naca4ModifiedAirfoilParams(spec)
            raise ValueError(
                "Modified NACA 4-digit specs must provide exactly one of "
                "designation or params."
            )
        if isinstance(spec, Naca5AirfoilSpec):
            if spec.designation is not None and spec.params is None:
                return Naca5AirfoilClassic(spec)
            if spec.designation is None and spec.params is not None:
                return Naca5AirfoilParams(spec)
            raise ValueError(
                "NACA 5-digit specs must provide exactly one of designation "
                "or params."
            )
        if isinstance(spec, Naca5ModifiedAirfoilSpec):
            if spec.designation is not None and spec.params is None:
                return Naca5ModifiedAirfoilClassic(spec)
            if spec.designation is None and spec.params is not None:
                return Naca5ModifiedAirfoilParams(spec)
            raise ValueError(
                "Modified NACA 5-digit specs must provide exactly one of "
                "designation or params."
            )

        raise NotImplementedError(
            f"AirfoilFactory does not support {type(spec).__name__} yet."
        )

    @staticmethod
    def naca4_from_designation(
        designation: str,
    ) -> Naca4AirfoilClassic:
        """
        Build a classic NACA 4-digit airfoil from a designation.

        Parameters
        ----------
        designation : str
            Four-digit NACA designation such as ``"2412"``.

        Returns
        -------
        Naca4AirfoilClassic
            Runtime airfoil built from the classic designation.

        Raises
        ------
        ValueError
            If ``designation`` is not a valid 4-digit NACA code.
        """
        return Naca4AirfoilClassic.from_designation(designation)

    @staticmethod
    def naca4_from_params(
        *,
        m: float,
        p: float,
        t: float,
        trailing_edge: Literal["standard", "sharp"] = "standard",
        leading_edge_radius: Literal["standard", "exact"] = "standard",
    ) -> Naca4AirfoilParams:
        """
        Build a parametric NACA 4-digit airfoil from explicit params.

        Parameters
        ----------
        m : float
            Maximum camber as a fraction of chord.
        p : float
            Chordwise location of maximum camber as a fraction of chord.
        t : float
            Maximum thickness as a fraction of chord.
        trailing_edge : {"standard", "sharp"}, default="standard"
            Trailing-edge closure model for the thickness distribution.
        leading_edge_radius : {"standard", "exact"}, default="standard"
            Leading-edge radius treatment for the thickness distribution.

        Returns
        -------
        Naca4AirfoilParams
            Runtime airfoil built from the explicit parameters.

        Raises
        ------
        ValueError
            If any parameter lies outside the supported NACA 4-digit range.
        """
        params = Naca4AirfoilParamsSpec(
            m=m,
            p=p,
            t=t,
            trailing_edge=trailing_edge,
            leading_edge_radius=leading_edge_radius,
        )
        return Naca4AirfoilParams(Naca4AirfoilSpec(params=params))

    @staticmethod
    def naca4_modified_from_designation(
        designation: str,
    ) -> Naca4ModifiedAirfoilClassic:
        """
        Build a classic modified NACA 4-digit airfoil from a designation.

        Parameters
        ----------
        designation : str
            Modified NACA 4-digit designation such as ``"0003-64"``.

        Returns
        -------
        Naca4ModifiedAirfoilClassic
            Runtime airfoil built from the modified-series designation.

        Raises
        ------
        ValueError
            If ``designation`` is not a valid modified NACA 4-digit code.
        """
        return Naca4ModifiedAirfoilClassic.from_designation(designation)

    @staticmethod
    def naca4_modified_from_params(
        *,
        m: float,
        p: float,
        t: float,
        leading_edge_index: float,
        max_thickness_location: float,
        trailing_edge: Literal["standard", "sharp"] = "standard",
    ) -> Naca4ModifiedAirfoilParams:
        """
        Build a parametric modified NACA 4-digit airfoil from params.

        Parameters
        ----------
        m : float
            Maximum camber as a fraction of chord.
        p : float
            Chordwise location of maximum camber as a fraction of chord.
        t : float
            Maximum thickness as a fraction of chord.
        leading_edge_index : float
            Modified-series leading-edge shape index.
        max_thickness_location : float
            Chordwise location of maximum thickness as a fraction of chord.
        trailing_edge : {"standard", "sharp"}, default="standard"
            Trailing-edge closure model for the thickness distribution.

        Returns
        -------
        Naca4ModifiedAirfoilParams
            Runtime airfoil built from the explicit parameters.

        Raises
        ------
        ValueError
            If any parameter lies outside the supported modified-series
            range.
        """
        params = Naca4ModifiedAirfoilParamsSpec(
            m=m,
            p=p,
            t=t,
            leading_edge_index=leading_edge_index,
            max_thickness_location=max_thickness_location,
            trailing_edge=trailing_edge,
        )
        return Naca4ModifiedAirfoilParams(
            Naca4ModifiedAirfoilSpec(params=params)
        )

    @staticmethod
    def naca5_from_designation(
        designation: str,
    ) -> Naca5AirfoilClassic:
        """
        Build a classic NACA 5-digit airfoil from a designation.

        Parameters
        ----------
        designation : str
            Five-digit NACA designation such as ``"23012"``.

        Returns
        -------
        Naca5AirfoilClassic
            Runtime airfoil built from the classic designation.

        Raises
        ------
        ValueError
            If ``designation`` is not a valid 5-digit NACA code.
        """
        return Naca5AirfoilClassic.from_designation(designation)

    @staticmethod
    def naca5_from_params(
        *,
        ideal_lift_coefficient: float,
        max_camber_location: float,
        reflexed: bool,
        t: float,
        trailing_edge: Literal["standard", "sharp"] = "standard",
        leading_edge_radius: Literal["standard", "exact"] = "standard",
    ) -> Naca5AirfoilParams:
        """
        Build a parametric NACA 5-digit airfoil from explicit params.

        Parameters
        ----------
        ideal_lift_coefficient : float
            Design lift coefficient associated with the camber line.
        max_camber_location : float
            Chordwise location of maximum camber as a fraction of chord.
        reflexed : bool
            Whether to build the reflexed camber-line family.
        t : float
            Maximum thickness as a fraction of chord.
        trailing_edge : {"standard", "sharp"}, default="standard"
            Trailing-edge closure model for the thickness distribution.
        leading_edge_radius : {"standard", "exact"}, default="standard"
            Leading-edge radius treatment for the thickness distribution.

        Returns
        -------
        Naca5AirfoilParams
            Runtime airfoil built from the explicit parameters.

        Raises
        ------
        ValueError
            If any parameter lies outside the supported NACA 5-digit range.
        """
        params = Naca5AirfoilParamsSpec(
            ideal_lift_coefficient=ideal_lift_coefficient,
            max_camber_location=max_camber_location,
            reflexed=reflexed,
            t=t,
            trailing_edge=trailing_edge,
            leading_edge_radius=leading_edge_radius,
        )
        return Naca5AirfoilParams(Naca5AirfoilSpec(params=params))

    @staticmethod
    def naca5_modified_from_designation(
        designation: str,
    ) -> Naca5ModifiedAirfoilClassic:
        """
        Build a classic modified NACA 5-digit airfoil from a designation.

        Parameters
        ----------
        designation : str
            Modified NACA 5-digit designation string.

        Returns
        -------
        Naca5ModifiedAirfoilClassic
            Runtime airfoil built from the modified-series designation.

        Raises
        ------
        ValueError
            If ``designation`` is not a valid modified NACA 5-digit code.
        """
        return Naca5ModifiedAirfoilClassic.from_designation(designation)

    @staticmethod
    def naca5_modified_from_params(
        *,
        ideal_lift_coefficient: float,
        max_camber_location: float,
        reflexed: bool,
        t: float,
        leading_edge_index: float,
        max_thickness_location: float,
        trailing_edge: Literal["standard", "sharp"] = "standard",
    ) -> Naca5ModifiedAirfoilParams:
        """
        Build a parametric modified NACA 5-digit airfoil from params.

        Parameters
        ----------
        ideal_lift_coefficient : float
            Design lift coefficient associated with the camber line.
        max_camber_location : float
            Chordwise location of maximum camber as a fraction of chord.
        reflexed : bool
            Whether to build the reflexed camber-line family.
        t : float
            Maximum thickness as a fraction of chord.
        leading_edge_index : float
            Modified-series leading-edge shape index.
        max_thickness_location : float
            Chordwise location of maximum thickness as a fraction of chord.
        trailing_edge : {"standard", "sharp"}, default="standard"
            Trailing-edge closure model for the thickness distribution.

        Returns
        -------
        Naca5ModifiedAirfoilParams
            Runtime airfoil built from the explicit parameters.

        Raises
        ------
        ValueError
            If any parameter lies outside the supported modified-series
            range.
        """
        params = Naca5ModifiedAirfoilParamsSpec(
            ideal_lift_coefficient=ideal_lift_coefficient,
            max_camber_location=max_camber_location,
            reflexed=reflexed,
            t=t,
            leading_edge_index=leading_edge_index,
            max_thickness_location=max_thickness_location,
            trailing_edge=trailing_edge,
        )
        return Naca5ModifiedAirfoilParams(
            Naca5ModifiedAirfoilSpec(params=params)
        )


__all__ = ["AirfoilFactory"]
