from collections.abc import Mapping
from logging import Logger, getLogger

from hypomnema.base.errors import MissingHandlerError
from hypomnema.base.types import (BaseElement, Bpt, Ept, Header, Hi, It, Note,
                                  Ph, Prop, Sub, Tmx, Tu, Tuv)
from hypomnema.xml.backends.base import XmlBackend
from hypomnema.xml.policy import XmlPolicy
from hypomnema.xml.serialization._handlers import (BptSerializer,
                                                   EptSerializer,
                                                   HeaderSerializer,
                                                   HiSerializer, ItSerializer,
                                                   NoteSerializer,
                                                   PhSerializer,
                                                   PropSerializer,
                                                   SubSerializer,
                                                   TmxSerializer, TuSerializer,
                                                   TuvSerializer)
from hypomnema.xml.serialization.base import BaseElementSerializer

__all__ = ["Serializer"]


class Serializer[TypeOfBackendElement]:
  def __init__(
    self,
    backend: XmlBackend[TypeOfBackendElement],
    policy: XmlPolicy | None = None,
    logger: Logger | None = None,
    handlers: Mapping[type, BaseElementSerializer] | None = None,
  ):
    self.backend: XmlBackend[TypeOfBackendElement] = backend
    self.policy: XmlPolicy = policy or XmlPolicy()
    self.logger: Logger = logger or getLogger(str(self))
    if handlers is None:
      self.logger.debug("Using default handlers")
      handlers = self._get_default_handlers()
    else:
      self.logger.debug("Using custom handlers")
    self.handlers = handlers

    for handler in self.handlers.values():
      if handler._emit is None:
        handler._set_emit(self.serialize)

  def _get_default_handlers(self) -> dict[type, BaseElementSerializer]:
    return {
      Note: NoteSerializer(self.backend, self.policy, self.logger),
      Prop: PropSerializer(self.backend, self.policy, self.logger),
      Header: HeaderSerializer(self.backend, self.policy, self.logger),
      Tu: TuSerializer(self.backend, self.policy, self.logger),
      Tuv: TuvSerializer(self.backend, self.policy, self.logger),
      Bpt: BptSerializer(self.backend, self.policy, self.logger),
      Ept: EptSerializer(self.backend, self.policy, self.logger),
      It: ItSerializer(self.backend, self.policy, self.logger),
      Ph: PhSerializer(self.backend, self.policy, self.logger),
      Sub: SubSerializer(self.backend, self.policy, self.logger),
      Hi: HiSerializer(self.backend, self.policy, self.logger),
      Tmx: TmxSerializer(self.backend, self.policy, self.logger),
    }

  def serialize(self, obj: BaseElement) -> TypeOfBackendElement | None:
    obj_type = type(obj)
    self.logger.debug("Serializing %r", obj_type)
    handler = self.handlers.get(obj_type)
    if handler is None:
      self.logger.log(
        self.policy.missing_serialization_handler.log_level, "Missing handler for %r", obj_type
      )
      if self.policy.missing_serialization_handler.behavior == "raise":
        raise MissingHandlerError(f"Missing handler for {obj_type!r}") from None
      elif self.policy.missing_serialization_handler.behavior == "ignore":
        return None
      else:
        self.logger.log(
          self.policy.missing_serialization_handler.log_level,
          "Falling back to default handler for %r",
          obj_type,
        )
        handler = self._get_default_handlers().get(obj_type)
        if handler is None:
          raise MissingHandlerError(f"Missing handler for {obj_type!r}") from None
    return handler._serialize(obj)
