from abc import abstractmethod
from typing import ClassVar, Literal
from typing_extensions import Self

from numpy import ndarray

from .._typing import ArrayLike, Int, MatrixLike
from ..base import BaseEstimator, ClassifierMixin, RegressorMixin, TransformerMixin
from ..preprocessing import LabelEncoder
from ..utils import Bunch
from ._base import _BaseHeterogeneousEnsemble

class _BaseVoting(TransformerMixin, _BaseHeterogeneousEnsemble):
    _parameter_constraints: ClassVar[dict] = ...

    @abstractmethod
    def fit(self, X: ndarray, y: ndarray, sample_weight=None): ...
    def fit_transform(self, X: MatrixLike, y: None | ArrayLike = None, **fit_params) -> ndarray: ...
    def n_features_in_(self): ...

class VotingClassifier(ClassifierMixin, _BaseVoting):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    classes_: ndarray = ...
    le_: LabelEncoder = ...
    named_estimators_: Bunch = ...
    estimators_: list[ClassifierMixin] = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(
        self,
        estimators: list[tuple[str, BaseEstimator]],
        *,
        voting: Literal["hard", "soft"] = "hard",
        weights: None | ArrayLike = None,
        n_jobs: None | Int = None,
        flatten_transform: bool = True,
        verbose: bool = False,
    ) -> None: ...
    def fit(
        self,
        X: MatrixLike | ArrayLike,
        y: ArrayLike,
        sample_weight: None | ArrayLike = None,
    ) -> Self: ...
    def predict(self, X: MatrixLike | ArrayLike) -> ArrayLike: ...
    def predict_proba(self, X: MatrixLike | ArrayLike) -> ndarray: ...
    def transform(self, X: MatrixLike | ArrayLike): ...
    def get_feature_names_out(self, input_features: None | ArrayLike = None) -> ndarray: ...

class VotingRegressor(RegressorMixin, _BaseVoting):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    named_estimators_: Bunch = ...
    estimators_: list[RegressorMixin] = ...

    def __init__(
        self,
        estimators: list[tuple[str, BaseEstimator]],
        *,
        weights: None | ArrayLike = None,
        n_jobs: None | Int = None,
        verbose: bool = False,
    ) -> None: ...
    def fit(
        self,
        X: MatrixLike | ArrayLike,
        y: ArrayLike,
        sample_weight: None | ArrayLike = None,
    ) -> Self: ...
    def predict(self, X: MatrixLike | ArrayLike) -> ndarray: ...
    def transform(self, X: MatrixLike | ArrayLike) -> ndarray: ...
    def get_feature_names_out(self, input_features: None | ArrayLike = None) -> ndarray: ...
