"""Interface definition for serializers."""

import abc
from pathlib import Path
from typing import ClassVar

from mtg_ssm.containers.collection import MagicCollection
from mtg_ssm.containers.indexes import Oracle


class Error(Exception):
    """Base error for serializers."""


class UnknownDialectError(Exception):
    """Raised when an (extension, dialect) pair is requested."""


class DeserializationError(Error):
    """Raised when there is an error reading counts from a file."""


class SerializationDialect(metaclass=abc.ABCMeta):
    """Abstract interface for mtg ssm serialization dialect."""

    _EXT_DIALECT_DOC: ClassVar[set[tuple[str, str, str]]] = set()
    _EXT_DIALECT_TO_IMPL: ClassVar[dict[tuple[str, str], type["SerializationDialect"]]] = {}

    extension: ClassVar[str | None] = None
    dialect: ClassVar[str | None] = None

    def __init_subclass__(cls: type["SerializationDialect"]) -> None:
        super().__init_subclass__()
        if cls.extension is not None and cls.dialect is not None:
            cls._EXT_DIALECT_DOC.add((cls.extension, cls.dialect, cls.__doc__ or cls.__name__))
            cls._EXT_DIALECT_TO_IMPL[(cls.extension, cls.dialect)] = cls

    @abc.abstractmethod
    def write(self, path: Path, collection: MagicCollection) -> None:
        """Write print counts to a file."""

    @abc.abstractmethod
    def read(self, path: Path, oracle: Oracle) -> MagicCollection:
        """Read print counts from file."""

    @classmethod
    def dialects(
        cls: type["SerializationDialect"],
    ) -> list[tuple[str, str | None, str | None]]:
        """List of (extension, dialect, description) of registered dialects."""
        return sorted((ext, dial or "", doc or "") for ext, dial, doc in cls._EXT_DIALECT_DOC)

    @classmethod
    def by_extension(
        cls: type["SerializationDialect"],
        extension: str,
        dialect_mappings: dict[str, str],
    ) -> type["SerializationDialect"]:
        """Get a serializer class for a given extension and dialect mapping."""
        dialect = dialect_mappings.get(extension, extension)
        try:
            return cls._EXT_DIALECT_TO_IMPL[(extension, dialect)]
        except KeyError as err:
            msg = f'File extension: "{extension}" dialect: "{dialect}" not found in registry'
            raise UnknownDialectError(msg) from err
