from collections.abc import Iterable
from typing import Callable, ClassVar
from typing_extensions import Self

from numpy import ndarray

from .._typing import ArrayLike, Int, MatrixLike
from ..base import BaseEstimator, MetaEstimatorMixin
from ..linear_model._logistic import LogisticRegression
from ..model_selection import BaseCrossValidator
from ._base import SelectorMixin

# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>
#          Vincent Michel <vincent.michel@inria.fr>
#          Gilles Louppe <g.louppe@gmail.com>
#
# License: BSD 3 clause

class RFE(SelectorMixin, MetaEstimatorMixin, BaseEstimator):
    support_: ndarray = ...
    ranking_: ndarray = ...
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    n_features_: int = ...
    estimator_: BaseEstimator = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(
        self,
        estimator: BaseEstimator,
        *,
        n_features_to_select: float | None = None,
        step: float = 1,
        verbose: Int = 0,
        importance_getter: str | Callable = "auto",
    ) -> None: ...
    @property
    def classes_(self) -> ndarray: ...
    def fit(self, X: MatrixLike | ArrayLike, y: ArrayLike, **fit_params) -> Self: ...
    def predict(self, X: MatrixLike) -> ndarray: ...
    def score(self, X: MatrixLike, y: ArrayLike, **fit_params) -> float: ...
    def decision_function(self, X: MatrixLike) -> ndarray: ...
    def predict_proba(self, X: MatrixLike) -> ndarray: ...
    def predict_log_proba(self, X: MatrixLike) -> ndarray: ...

class RFECV(RFE):
    support_: ndarray = ...
    ranking_: ndarray = ...
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    n_features_: int = ...
    cv_results_: dict[str, ndarray] = ...
    estimator_: BaseEstimator = ...
    classes_: ndarray = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(
        self,
        estimator: BaseEstimator | LogisticRegression,
        *,
        step: float = 1,
        min_features_to_select: Int = 1,
        cv: int | BaseCrossValidator | Iterable | None = None,
        scoring: None | str | Callable = None,
        verbose: Int = 0,
        n_jobs: None | int = None,
        importance_getter: str | Callable = "auto",
    ) -> None: ...
    def fit(
        self,
        X: MatrixLike | ArrayLike,
        y: ArrayLike,
        groups: None | ArrayLike = None,
    ) -> Self: ...
