import array
import itertools
import warnings
from numbers import Integral as Integral, Real as Real
from typing import ClassVar
from typing_extensions import Self

import numpy as np
import scipy.sparse as sp
from numpy import ndarray
from numpy.random import RandomState

from ._typing import ArrayLike, Float, Int, MatrixLike
from .base import (
    BaseEstimator,
    ClassifierMixin,
    MetaEstimatorMixin,
    MultiOutputMixin,
    clone as clone,
    is_classifier as is_classifier,
    is_regressor as is_regressor,
)
from .metrics.pairwise import euclidean_distances as euclidean_distances
from .preprocessing import LabelBinarizer
from .utils import check_random_state as check_random_state
from .utils._param_validation import HasMethods as HasMethods, Interval as Interval
from .utils.metaestimators import available_if as available_if
from .utils.multiclass import check_classification_targets as check_classification_targets
from .utils.parallel import Parallel as Parallel, delayed as delayed
from .utils.validation import check_is_fitted as check_is_fitted

# Author: Mathieu Blondel <mathieu@mblondel.org>
# Author: Hamzeh Alsalhi <93hamsal@gmail.com>
#
# License: BSD 3 clause

__all__ = [
    "OneVsRestClassifier",
    "OneVsOneClassifier",
    "OutputCodeClassifier",
]

class _ConstantPredictor(BaseEstimator):
    def fit(self, X, y): ...
    def predict(self, X): ...
    def decision_function(self, X): ...
    def predict_proba(self, X): ...

class OneVsRestClassifier(MultiOutputMixin, ClassifierMixin, MetaEstimatorMixin, BaseEstimator):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    label_binarizer_: LabelBinarizer = ...
    classes_: ndarray = ...
    estimators_: list[BaseEstimator] = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(self, estimator: BaseEstimator, *, n_jobs: None | Int = None, verbose: Int = 0) -> None: ...
    def fit(
        self,
        X: MatrixLike | ArrayLike,
        y: MatrixLike | ArrayLike,
    ) -> Self: ...
    def partial_fit(
        self,
        X: MatrixLike | ArrayLike,
        y: MatrixLike | ArrayLike,
        classes: None | ArrayLike = None,
    ) -> Self: ...
    def predict(self, X: MatrixLike | ArrayLike) -> ndarray: ...
    def predict_proba(self, X: MatrixLike | ArrayLike) -> ndarray: ...
    def decision_function(self, X: MatrixLike) -> ndarray: ...
    @property
    def multilabel_(self) -> bool: ...
    @property
    def n_classes_(self) -> int: ...

class OneVsOneClassifier(MetaEstimatorMixin, ClassifierMixin, BaseEstimator):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    pairwise_indices_: None | list = ...
    classes_: ndarray = ...
    estimators_: list[BaseEstimator] = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(self, estimator: BaseEstimator, *, n_jobs: None | Int = None) -> None: ...
    def fit(self, X: MatrixLike | ArrayLike, y: ArrayLike) -> Self: ...
    def partial_fit(
        self,
        X: MatrixLike,
        y: ArrayLike,
        classes: None | ArrayLike = None,
    ) -> Self: ...
    def predict(self, X: MatrixLike | ArrayLike) -> ndarray: ...
    def decision_function(self, X: MatrixLike) -> ndarray: ...
    @property
    def n_classes_(self) -> int: ...

class OutputCodeClassifier(MetaEstimatorMixin, ClassifierMixin, BaseEstimator):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    code_book_: ndarray = ...
    classes_: ndarray = ...
    estimators_: list[BaseEstimator] = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(
        self,
        estimator: BaseEstimator,
        *,
        code_size: Float = 1.5,
        random_state: RandomState | None | Int = None,
        n_jobs: None | Int = None,
    ) -> None: ...
    def fit(self, X: MatrixLike | ArrayLike, y: ArrayLike) -> Self: ...
    def predict(self, X: MatrixLike | ArrayLike) -> ndarray: ...
