from typing import Generic, Optional, Self, TypeVarTuple

import numpy as np
from numpy.typing import NDArray as NDArray

_Xs = TypeVarTuple("_Xs")

class ParametricModel:
    _params: Parameters

    def __init__(self, **kwparams: Optional[float]) -> None: ...
    @property
    def params(self) -> NDArray[np.float64]: ...

    # noinspection PyUnresolvedReferences
    @params.setter
    def params(self, new_params: NDArray[np.float64]): ...
    @property
    def params_names(self) -> tuple[str, ...]: ...
    @property
    def nb_params(self) -> int: ...

class Parameters:
    _parent: Optional[Self]
    _leaves = dict[str, Self]
    _nodemapping = dict[str, float]
    _values: tuple[float, ...]
    _names: tuple[str, ...]

    def __init__(self, **kwargs: Optional[float]) -> None: ...
    def get_names(self) -> tuple[str, ...]: ...
    def get_values(self) -> tuple[float, ...]: ...
    def get_leaf(self, name: str) -> Parameters: ...
    def get_param_value(self, name: str) -> float: ...
    def set_param_value(self, name: str, value: float) -> None: ...
    @property
    def size(self) -> int: ...
    def set_node(self, mapping: dict[str, float | None]) -> None: ...
    def set_leaf(self, leaf_name: str, leaf: Parameters) -> None: ...
    def set_values(self, values: tuple[float, ...]) -> None: ...
    def _update_names_and_values(self) -> None: ...

class FrozenParametricModel(ParametricModel, Generic[*_Xs]):
    nb_assets: int
    args: tuple[*_Xs]
    unfrozen_model: ParametricModel
    def __init__(self, model: ParametricModel, *args: *_Xs) -> None: ...
    def unfreeze(self) -> ParametricModel: ...

def is_frozen(model: ParametricModel) -> bool: ...
def is_lifetime_model(model: ParametricModel) -> bool: ...
def is_stochastic_process(model: ParametricModel) -> bool: ...
