import datetime
from typing import Any
from typing import Dict
from typing import Optional
from typing import Union

from xsdata.formats.dataclass.parsers import DictDecoder

from ... import errors
from ..models.xsd.investigation import Investigation
from . import xml


def serialize_investigation_message(
    beamline: str,
    proposal: str,
    start_datetime: Optional[Union[datetime.datetime, str]] = None,
    end_datetime: Optional[Union[datetime.datetime, str]] = None,
    investigation_id: Optional[str] = None,
    strict: bool = False,
    indent: Optional[str] = None,
) -> bytes:
    """
    :raises IcatMetadataValidationError: If ``strict=True`` and the metadata is invalid.
    :emits IcatMetadataValidationWarning: If ``strict=False`` and the metadata is invalid.
    :emits IcatMetadataDeprecatedWarning: If metadata is deprecated.
    """
    if start_datetime is None:
        start_datetime = datetime.datetime.now().astimezone()

    if end_datetime is not None:
        # This was added for the unit tests but fails XSD validation.
        # Not used in production.
        message = "ICAT Investigation schema does not have 'end_datetime'. Ignore it."
        errors.invalid_warn_or_raise(message, strict=strict)

    # XML message root
    root = {
        "experiment": proposal,
        "instrument": beamline,
        "startDate": start_datetime,
        "investigationId": investigation_id,  # Trick XSD validation when None
    }

    # Serialize before XSD serialization
    root = xml.serialize_root(root)

    # Serialize with XSD validation
    return raw_serialize_investigation_message(root, indent=indent)


def raw_serialize_investigation_message(
    data: Dict[str, Any], indent: Optional[str] = None
) -> bytes:
    """Serialize message with XSD 'investigation' validation."""
    model = DictDecoder().decode(data, Investigation)
    return xml.serialize_message(model, indent=indent)
