from __future__ import annotations

import logging
from typing import TYPE_CHECKING, Literal, Mapping, Optional, Union

from great_expectations._docs_decorators import public_api
from great_expectations.compatibility.typing_extensions import override
from great_expectations.data_context.data_context.abstract_data_context import (
    AbstractDataContext,
)
from great_expectations.data_context.data_context_variables import (
    EphemeralDataContextVariables,
)
from great_expectations.data_context.migrator.file_migrator import FileMigrator

if TYPE_CHECKING:
    from great_expectations.data_context.data_context.file_data_context import (
        FileDataContext,
    )
    from great_expectations.data_context.store.datasource_store import DatasourceStore
    from great_expectations.data_context.types.base import (
        DataContextConfig,
    )

logger = logging.getLogger(__name__)


@public_api
class EphemeralDataContext(AbstractDataContext):
    """Subclass of AbstractDataContext that uses runtime values to generate a temporary or in-memory DataContext."""  # noqa: E501 # FIXME CoP

    def __init__(
        self,
        project_config: Union[DataContextConfig, Mapping],
        runtime_environment: Optional[dict] = None,
        user_agent_str: str | None = None,
    ) -> None:
        """EphemeralDataContext constructor

        project_config: config for in-memory EphemeralDataContext
        runtime_environment: a dictionary of config variables tha
                override both those set in config_variables.yml and the environment

        """
        self._project_config = self._init_project_config(project_config)
        super().__init__(runtime_environment=runtime_environment, user_agent_str=user_agent_str)

    @property
    @override
    def mode(self) -> Literal["ephemeral"]:
        return "ephemeral"

    @override
    def _init_project_config(
        self, project_config: Union[DataContextConfig, Mapping]
    ) -> DataContextConfig:
        return EphemeralDataContext.get_or_create_data_context_config(project_config)

    @override
    def _init_variables(self) -> EphemeralDataContextVariables:
        variables = EphemeralDataContextVariables(
            config=self._project_config,
            config_provider=self.config_provider,
        )
        return variables

    @override
    def _init_datasource_store(self) -> DatasourceStore:
        from great_expectations.data_context.store.datasource_store import (
            DatasourceStore,
        )

        store_name: str = "datasource_store"  # Never explicitly referenced but adheres
        # to the convention set by other internal Stores
        store_backend: dict = {"class_name": "InMemoryStoreBackend"}

        datasource_store = DatasourceStore(
            store_name=store_name,
            store_backend=store_backend,
        )

        return datasource_store

    @public_api
    def convert_to_file_context(self) -> FileDataContext:
        """Convert existing EphemeralDataContext into a FileDataContext.

        Scaffolds a file-backed project structure in the current working directory.

        Returns:
            A FileDataContext with an updated config to reflect the state of the
            current context.
        """
        self._synchronize_fluent_datasources()
        migrator = FileMigrator(
            primary_stores=self.stores,
            datasource_store=self._datasource_store,
            variables=self.variables,
            fluent_config=self.fluent_config,
        )
        return migrator.migrate()
