import logging

from ...exceptions import NoDatabaseProvidedException
from ...utils import LocalStorage, write_summary
from ...utils.constants import ENCODING_UTF8
from ..abstract import (
    CATALOG_ASSETS,
    AssetsMapping,
    WarehouseAsset,
    WarehouseAssetGroup,
    common_args,
)
from .client import GlueAthenaClient
from .credentials import GlueAthenaCredentials

logger = logging.getLogger(__name__)

GLUE_ATHENA_ASSETS: AssetsMapping = {
    WarehouseAssetGroup.CATALOG: CATALOG_ASSETS,
    WarehouseAssetGroup.ROLE: (WarehouseAsset.USER,),
}


def _credentials(params: dict) -> GlueAthenaCredentials:
    """Resolve credentials for Glue/Athena access."""
    filtered = {
        key: value for key, value in params.items() if value is not None
    }
    return GlueAthenaCredentials(**filtered)


def _write_empty_csv(storage: LocalStorage, asset: WarehouseAsset) -> str:
    """Write an empty CSV file for ingestion requirements."""
    path = storage.path(asset.value)
    with open(path, "w", encoding=ENCODING_UTF8) as file:
        file.write("")
    return path


class GlueAthenaExtractionProcessor:
    """Orchestrate Glue/Athena extraction into local storage."""

    def __init__(
        self,
        client: GlueAthenaClient,
        storage: LocalStorage,
    ) -> None:
        self._client = client
        self._storage = storage

    def extract_catalog(self) -> dict[str, str]:
        """Extract catalog assets from Glue."""
        catalog_locations: dict[str, str] = {}

        database_assets = self._client.databases()
        location = self._storage.put(
            WarehouseAsset.DATABASE.value, database_assets
        )
        catalog_locations[WarehouseAsset.DATABASE.value] = location
        logger.info(
            "Extracted %s databases to %s",
            len(database_assets),
            location,
        )

        schemas = self._client.schemas()
        if not schemas:
            raise NoDatabaseProvidedException

        location = self._storage.put(WarehouseAsset.SCHEMA.value, schemas)
        catalog_locations[WarehouseAsset.SCHEMA.value] = location
        logger.info(
            "Extracted %s schemas to %s",
            len(schemas),
            location,
        )

        tables, columns = self._client.tables_and_columns(schemas)

        location = self._storage.put(WarehouseAsset.TABLE.value, tables)
        catalog_locations[WarehouseAsset.TABLE.value] = location
        logger.info("Extracted %s tables to %s", len(tables), location)

        location = self._storage.put(WarehouseAsset.COLUMN.value, columns)
        catalog_locations[WarehouseAsset.COLUMN.value] = location
        logger.info("Extracted %s columns to %s", len(columns), location)
        return catalog_locations


def extract_all(**kwargs) -> None:
    """Run the full Glue/Athena extraction flow."""
    output_directory, _ = common_args(kwargs)

    credentials = _credentials(kwargs)
    client = GlueAthenaClient(credentials=credentials)

    storage = LocalStorage(directory=output_directory)

    extractor = GlueAthenaExtractionProcessor(
        client=client,
        storage=storage,
    )

    extractor.extract_catalog()
    # Glue/Athena does not provide warehouse users/roles in this extractor.
    # We still emit an empty USER file to keep role ingestion contracts
    # consistent with other technologies where this file is mandatory.
    _write_empty_csv(storage, WarehouseAsset.USER)

    write_summary(
        output_directory,
        storage.stored_at_ts,
        client_name="glue_athena",
    )
