# This file is part of Cantera. See License.txt in the top-level directory or
# at https://cantera.org/license.txt for license and copyright information.

from collections.abc import Callable, Sequence
from pathlib import Path
from typing import (
    Any,
    ClassVar,
    Literal,
    TypedDict,
    overload,
)

from typing_extensions import Never, override

from ._types import (
    Array,
    ArrayLike,
    Basis,
    CompositionLike,
    CompressionLevel,
    LogLevel,
    RefineCriteria,
)
from .jacobians import SystemJacobian
from .solutionbase import SolutionArrayBase, _SolutionBase
from .transport import _TransportModel

_ToleranceSettings = TypedDict(
    "_ToleranceSettings",
    {
        "transient-abstol": float,
        "steady-abstol": float,
        "transient-reltol": float,
        "steady-reltol": float,
    },
)

class _PhaseSettings(TypedDict):
    name: str
    source: str

class _FixedPointSettings(TypedDict):
    location: float
    temperature: float

_Domain1DSettings = TypedDict(
    "_Domain1DSettings",
    {
        "type": str,
        "points": int,
        "tolerances": _ToleranceSettings,
        "transport-model": _TransportModel,
        "phase": _PhaseSettings,
        "radiation-enabled": bool,
        "energy-enabled": bool,
        "Soret-enabled": bool,
        "flux-gradient-basis": Literal[0, 1],
        "refine-criteria": RefineCriteria,
        "fixed-point": _FixedPointSettings,
    },
)

class Domain1D:
    _domain_type: ClassVar[str]
    have_user_tolerances: bool
    def __init__(self, phase: _SolutionBase, *args: Any, **kwargs: Any) -> None: ...
    @property
    def phase(self) -> _SolutionBase: ...
    @property
    def index(self) -> int: ...
    @property
    def domain_type(self) -> str: ...
    @property
    def n_components(self) -> int: ...
    @property
    def n_points(self) -> int: ...
    def _to_array(
        self, dest: SolutionArrayBase, normalize: bool
    ) -> SolutionArrayBase: ...
    def _from_array(self, arr: SolutionArrayBase) -> None: ...
    def component_name(self, n: int) -> str: ...
    @property
    def component_names(self) -> list[str]: ...
    def component_index(self, name: str) -> int: ...
    def _has_component(self, name: str) -> bool: ...
    @overload
    def info(
        self,
        keys: Sequence[str] | None = None,
        rows: int = 10,
        width: int | None = None,
        display: Literal[False] = False,
    ) -> str: ...
    @overload
    def info(
        self,
        keys: Sequence[str] | None = None,
        rows: int = 10,
        width: int | None = None,
        display: bool = True,
    ) -> None: ...
    def update_state(self, loc: int) -> None: ...
    @property
    def grid(self) -> Array: ...
    @grid.setter
    def grid(self, grid: ArrayLike) -> None: ...
    def value(self, component: str) -> float: ...
    def set_value(self, component: str, value: float) -> None: ...
    def values(self, component: str) -> Array: ...
    def set_values(self, component: str, values: Array) -> None: ...
    def residuals(self, component: str) -> Array: ...
    def set_profile(
        self, component: str, positions: Sequence[float], values: Sequence[float]
    ) -> None: ...
    def set_flat_profile(self, component: str, value: float) -> None: ...
    def set_bounds(
        self,
        *,
        default: tuple[float, float] | None = None,
        Y: tuple[float, float] | None = None,
        **kwargs: tuple[float, float],
    ) -> None: ...
    def set_steady_tolerances(
        self,
        *,
        default: tuple[float, float] | None = None,
        Y: tuple[float, float] | None = None,
        abs: tuple[float, float] | None = None,
        rel: tuple[float, float] | None = None,
        **kwargs: tuple[float, float],
    ) -> None: ...
    def set_transient_tolerances(
        self,
        *,
        default: tuple[float, float] | None = None,
        Y: tuple[float, float] | None = None,
        abs: tuple[float, float] | None = None,
        rel: tuple[float, float] | None = None,
        **kwargs: tuple[float, float],
    ) -> None: ...
    def set_default_tolerances(self) -> None: ...
    def bounds(self, component: str) -> tuple[float, float]: ...
    def tolerances(self, component: str) -> tuple[float, float]: ...
    @overload
    def steady_reltol(self, component: str) -> float: ...
    @overload
    def steady_reltol(self, component: None = None) -> Array: ...
    @overload
    def steady_abstol(self, component: str) -> float: ...
    @overload
    def steady_abstol(self, component: None = None) -> Array: ...
    @overload
    def transient_reltol(self, component: str) -> float: ...
    @overload
    def transient_reltol(self, component: None = None) -> Array: ...
    @overload
    def transient_abstol(self, component: str) -> float: ...
    @overload
    def transient_abstol(self, component: None = None) -> Array: ...
    @property
    def name(self) -> str: ...
    @name.setter
    def name(self, name: str) -> None: ...
    @override
    def __reduce__(self) -> Never: ...
    def __copy__(self) -> Never: ...
    @property
    def settings(self) -> _Domain1DSettings: ...

class Boundary1D(Domain1D):
    def __init__(self, phase: _SolutionBase, name: str | None = None) -> None: ...
    @property
    def T(self) -> float: ...
    @T.setter
    def T(self, T: float) -> None: ...
    @property
    def mdot(self) -> float: ...
    @mdot.setter
    def mdot(self, mdot: float) -> None: ...
    @property
    def X(self) -> Array: ...
    @X.setter
    def X(self, X: CompositionLike) -> None: ...
    @property
    def Y(self) -> Array: ...
    @Y.setter
    def Y(self, Y: CompositionLike) -> None: ...
    @property
    def spread_rate(self) -> float: ...
    @spread_rate.setter
    def spread_rate(self, spread_rate: float) -> None: ...

class Inlet1D(Boundary1D): ...
class Outlet1D(Boundary1D): ...
class OutletReservoir1D(Boundary1D): ...
class SymmetryPlane1D(Boundary1D): ...
class Surface1D(Boundary1D): ...

class ReactingSurface1D(Boundary1D):
    surface: _SolutionBase
    @property
    def phase(self) -> _SolutionBase: ...
    @property
    def coverage_enabled(self) -> bool: ...
    @coverage_enabled.setter
    def coverage_enabled(self, value: bool) -> None: ...

class FlowBase(Domain1D):
    def __init__(self, *args: Any, **kwargs: Any) -> None: ...
    @property
    def P(self) -> float: ...
    @P.setter
    def P(self, P: float) -> None: ...
    @property
    def T(self) -> Array: ...
    @property
    def velocity(self) -> Array: ...
    @property
    def spread_rate(self) -> Array: ...
    @property
    def radial_pressure_gradient(self) -> Array: ...
    @property
    def electric_field(self) -> Array: ...
    @property
    def oxidizer_velocity(self) -> Array: ...
    @property
    def transport_model(self) -> str: ...
    @transport_model.setter
    def transport_model(self, model: str) -> None: ...
    @property
    def soret_enabled(self) -> bool: ...
    @soret_enabled.setter
    def soret_enabled(self, enable: bool) -> None: ...
    @property
    def flux_gradient_basis(self) -> Basis: ...
    @flux_gradient_basis.setter
    def flux_gradient_basis(self, basis: Basis) -> None: ...
    @property
    def energy_enabled(self) -> bool: ...
    @energy_enabled.setter
    def energy_enabled(self, enable: bool) -> None: ...
    def set_fixed_temp_profile(self, pos: ArrayLike, T: ArrayLike) -> None: ...
    def get_settings3(self) -> _Domain1DSettings: ...
    @property
    def boundary_emissivities(self) -> tuple[float, float]: ...
    @boundary_emissivities.setter
    def boundary_emissivities(self, epsilon: tuple[float, float]) -> None: ...
    @property
    def radiation_enabled(self) -> bool: ...
    @radiation_enabled.setter
    def radiation_enabled(self, enable: bool) -> None: ...
    @property
    def radiative_heat_loss(self) -> Array: ...
    def set_free_flow(self) -> None: ...
    def set_axisymmetric_flow(self) -> None: ...
    @property
    def type(self) -> str: ...
    @property
    def solving_stage(self) -> Literal[1, 2]: ...
    @solving_stage.setter
    def solving_stage(self, stage: Literal[1, 2]) -> None: ...
    @property
    def electric_field_enabled(self) -> bool: ...
    @electric_field_enabled.setter
    def electric_field_enabled(self, enable: bool) -> None: ...
    @property
    def left_control_point_temperature(self) -> float: ...
    @left_control_point_temperature.setter
    def left_control_point_temperature(self, T: float) -> None: ...
    @property
    def right_control_point_temperature(self) -> float: ...
    @right_control_point_temperature.setter
    def right_control_point_temperature(self, T: float) -> None: ...
    @property
    def left_control_point_coordinate(self) -> float: ...
    @property
    def right_control_point_coordinate(self) -> float: ...
    @property
    def two_point_control_enabled(self) -> bool: ...
    @two_point_control_enabled.setter
    def two_point_control_enabled(self, enable: bool) -> None: ...

class FreeFlow(FlowBase): ...
class UnstrainedFlow(FlowBase): ...
class AxisymmetricFlow(FlowBase): ...

class Sim1D:
    domains: tuple[Domain1D]
    def __init__(
        self, domains: Sequence[Domain1D], *args: Any, **kwargs: Any
    ) -> None: ...
    def phase(self, domain: int | None = None) -> _SolutionBase: ...
    def set_interrupt(self, f: Callable[[float], float]) -> None: ...
    def set_time_step_callback(self, f: Callable[[float], float]) -> None: ...
    def set_steady_callback(self, f: Callable[[float], float]) -> None: ...
    def domain_index(self, dom: Domain1D | str | int) -> int: ...
    def value(
        self, domain: Domain1D | str | int, component: str | int, point: int
    ) -> float: ...
    def set_value(
        self,
        domain: Domain1D | str | int,
        component: str | int,
        point: int,
        value: float,
    ) -> None: ...
    def eval(self, rdt: float = 0.0) -> None: ...
    def work_value(
        self, domain: Domain1D | str | int, component: str | int, point: int
    ) -> float: ...
    def profile(self, domain: Domain1D | str | int, component: str | int) -> Array: ...
    def set_profile(
        self,
        domain: Domain1D | str | int,
        component: str | int,
        positions: Sequence[float],
        values: Sequence[float],
    ) -> None: ...
    def set_flat_profile(
        self,
        domain: Domain1D | str | int,
        component: str | int,
        value: float,
    ) -> None: ...
    def show(self) -> None: ...
    def set_time_step(self, stepsize: float, n_steps: Sequence[int]) -> None: ...
    @property
    def max_time_step_count(self) -> int: ...
    @max_time_step_count.setter
    def max_time_step_count(self, nmax: int) -> None: ...
    def set_jacobian_perturbation(
        self, relative: float, absolute: float, threshold: float
    ) -> None: ...
    @property
    def linear_solver(self) -> SystemJacobian: ...
    @linear_solver.setter
    def linear_solver(self, precon: SystemJacobian) -> None: ...
    def set_initial_guess(self, *args: Any, **kwargs: Any) -> None: ...
    def extinct(self) -> bool: ...
    def solve(
        self,
        loglevel: LogLevel = 1,
        refine_grid: bool = True,
        auto: bool = False,
    ) -> None: ...
    def refine(self, loglevel: LogLevel = 1) -> None: ...
    def set_refine_criteria(
        self,
        domain: Domain1D | str | int,
        ratio: float = 10.0,
        slope: float = 0.8,
        curve: float = 0.8,
        prune: float = 0.05,
    ) -> None: ...
    def get_refine_criteria(self, domain: Domain1D | str | int) -> RefineCriteria: ...
    def set_grid_min(
        self, dz: float, domain: Domain1D | str | int | None = None
    ) -> None: ...
    def set_max_jac_age(self, ss_age: int, ts_age: int) -> None: ...
    def set_time_step_factor(self, tfactor: float) -> None: ...
    def set_min_time_step(self, tsmin: float) -> None: ...
    def set_max_time_step(self, tsmax: float) -> None: ...
    @property
    def fixed_temperature(self) -> float: ...
    @fixed_temperature.setter
    def fixed_temperature(self, T: float) -> None: ...
    @property
    def fixed_temperature_location(self) -> float: ...
    def set_left_control_point(self, T: float) -> None: ...
    def set_right_control_point(self, T: float) -> None: ...
    def save(
        self,
        filename: Path | str = "soln.yaml",
        name: str = "solution",
        description: str | None = None,
        loglevel: LogLevel | None = None,
        *,
        overwrite: bool = False,
        compression: CompressionLevel = 0,
        basis: Basis | Literal["X", "Y"] | None = None,
    ) -> None: ...
    def restore(
        self,
        filename: Path | str = "soln.yaml",
        name: str = "solution",
        loglevel: LogLevel | None = None,
    ) -> dict[str, str]: ...
    def restore_time_stepping_solution(self) -> None: ...
    def restore_steady_solution(self) -> None: ...
    def show_stats(self, print_time: bool = True) -> None: ...
    def clear_stats(self) -> None: ...
    def solve_adjoint(
        self,
        perturb: Callable[[Sim1D, int, float], None],
        n_params: int,
        dgdx: Array,
        g: Callable[[Sim1D], float] | None = None,
        dp: float = 1e-5,
    ) -> None: ...
    @property
    def grid_size_stats(self) -> list[int]: ...
    @property
    def jacobian_time_stats(self) -> list[float]: ...
    @property
    def jacobian_count_stats(self) -> list[int]: ...
    @property
    def eval_time_stats(self) -> list[float]: ...
    @property
    def eval_count_stats(self) -> list[int]: ...
    @property
    def time_step_stats(self) -> list[int]: ...
    def set_max_grid_points(self, domain: Domain1D | str | int, npmax: int) -> None: ...
    def get_max_grid_points(self, domain: Domain1D | str | int) -> int: ...
