import sys
from typing import List, Optional, Sequence, Set, Tuple, Union

from django.db.backends.base.base import BaseDatabaseWrapper
from django.db.migrations.migration import Migration

from .loader import MigrationLoader
from .recorder import MigrationRecorder
from .state import ProjectState

if sys.version_info < (3, 8):
    from typing_extensions import Protocol
else:
    from typing import Protocol

class _ProgressCallbackT(Protocol):
    def __call__(self, __action: str, __migration: Optional[Migration] = ..., __fake: Optional[bool] = ...) -> None: ...

class MigrationExecutor:
    connection: BaseDatabaseWrapper = ...
    loader: MigrationLoader = ...
    recorder: MigrationRecorder = ...
    progress_callback: Optional[_ProgressCallbackT] = ...
    def __init__(
        self,
        connection: Optional[BaseDatabaseWrapper],
        progress_callback: Optional[_ProgressCallbackT] = ...,
    ) -> None: ...
    def migration_plan(
        self, targets: Union[Sequence[Tuple[str, Optional[str]]], Set[Tuple[str, str]]], clean_start: bool = ...
    ) -> List[Tuple[Migration, bool]]: ...
    def migrate(
        self,
        targets: Optional[Sequence[Tuple[str, Optional[str]]]],
        plan: Optional[Sequence[Tuple[Migration, bool]]] = ...,
        state: Optional[ProjectState] = ...,
        fake: bool = ...,
        fake_initial: bool = ...,
    ) -> ProjectState: ...
    def apply_migration(
        self, state: ProjectState, migration: Migration, fake: bool = ..., fake_initial: bool = ...
    ) -> ProjectState: ...
    def record_migration(self, migration: Migration) -> None: ...
    def unapply_migration(self, state: ProjectState, migration: Migration, fake: bool = ...) -> ProjectState: ...
    def check_replacements(self) -> None: ...
    def detect_soft_applied(
        self, project_state: Optional[ProjectState], migration: Migration
    ) -> Tuple[bool, ProjectState]: ...
