from datetime import datetime
from typing import Literal

from pydantic_xml import BaseXmlModel, attr, element, wrapped

# SOAP Envelope namespaces
NS_SOAP = 'http://www.w3.org/2003/05/soap-envelope'
NS_ADDR = 'http://www.w3.org/2005/08/addressing'
NS_WSSE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
NS_WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
NS_DS = 'http://www.w3.org/2000/09/xmldsig#'
NS_APP = 'http://egisz.rosminzdrav.ru/iehr/emdr/service/'

NSMAP = {
    's': NS_SOAP,
    'a': NS_ADDR,
    'wsse': NS_WSSE,
    'wsu': NS_WSU,
    'ds': NS_DS,
    'app': NS_APP,
}


class Reference(BaseXmlModel, tag='Reference', ns='ds', nsmap=NSMAP):
    uri: str = attr(name='URI')
    digest_method: Literal['http://www.w3.org/2001/04/xmlenc#sha256'] = (
        element(tag='DigestMethod', ns='ds', attr_name='Algorithm')
    )
    digest_value: str = element(tag='DigestValue', ns='ds')


class SignedInfo(BaseXmlModel, tag='SignedInfo', ns=NS_DS, nsmap=NSMAP):
    canon_method: Literal['http://www.w3.org/2001/10/xml-exc-c14n#'] = element(
        tag='CanonicalizationMethod', ns=NS_DS, attr_name='Algorithm'
    )
    sig_method: Literal[
        'urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256'
    ] = element(tag='SignatureMethod', ns=NS_DS, attr_name='Algorithm')
    references: list[Reference] = element(tag='Reference', ns=NS_DS)


class SecurityTokenReference(
    BaseXmlModel,
    tag='SecurityTokenReference',
    ns=NS_WSSE,
    nsmap={'wsse': NS_WSSE},
):
    ref_uri: str = wrapped('Reference', attr(name='URI'))
    ref_type: Literal[
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'
    ] = wrapped('Reference', attr(name='ValueType'))


class KeyInfo(BaseXmlModel, tag='KeyInfo', ns=NS_DS, nsmap=NSMAP):
    token_ref: SecurityTokenReference = element(
        tag='SecurityTokenReference', ns=NS_WSSE
    )


class Signature(BaseXmlModel, tag='Signature', ns=NS_DS, nsmap=NSMAP):
    signed_info: SignedInfo = element()
    signature_value: str = element(tag='SignatureValue')
    key_info: KeyInfo = element()


class BinarySecurityToken(
    BaseXmlModel,
    tag='BinarySecurityToken',
    ns=NS_WSSE,
    nsmap=NSMAP,
):
    id: str = attr(name='Id', ns=NS_WSU)
    value_type: Literal[
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'
    ] = attr(name='ValueType')
    encoding_type: Literal[
        'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'
    ] = attr(name='EncodingType')
    value: str


class Security(BaseXmlModel, tag='Security', ns=NS_WSSE, nsmap=NSMAP):
    token: BinarySecurityToken = element()
    signature: Signature = element()


class Action(BaseXmlModel, tag='Action', ns=NS_ADDR, nsmap=NSMAP):
    id: str = attr(name='Id', ns=NS_WSU)


class MessageID(BaseXmlModel, tag='MessageID', ns=NS_ADDR, nsmap=NSMAP):
    id: str = attr(name='Id', ns=NS_WSU)
    value: str


class Header(BaseXmlModel, tag='Header', ns=NS_SOAP, nsmap=NSMAP):
    action: Action | None = element(tag='Action', ns=NS_ADDR)
    message_id: MessageID | None = element(tag='MessageID', ns=NS_ADDR)
    security: Security = element(tag='Security', ns=NS_WSSE)


class Page(BaseXmlModel, tag='page', ns=NS_APP):
    items_per_page: int = element(tag='itemsPerPage')
    has_next: bool = element(tag='hasNext')


class Item(BaseXmlModel, tag='item', ns=NS_APP):
    emdr_id: str = element(tag='emdrId')
    local_uid: str = element(tag='localUid')
    registration_date: datetime = element(tag='registrationDate')
    registration_date_time: datetime = element(tag='registrationDateTime')
    store_till_date: str = element(tag='storeTillDate')
    doc_kind: str = element(tag='DocKind')
    is_semd: bool = element(tag='IsSemd')


class Matches(BaseXmlModel, tag='matches', ns=NS_APP):
    items: list[Item] = element(tag='item')
    page: Page = element()


class SearchRegistryItemResponse(
    BaseXmlModel,
    tag='searchRegistryItemResponse',
    ns=NS_APP,
    nsmap={'app': NS_APP},
):
    status: Literal['success'] = element()
    matches: Matches = element()


class Body(BaseXmlModel, tag='Body', ns=NS_SOAP, nsmap=NSMAP):
    id: str = attr(name='Id', ns=NS_WSU)
    response: SearchRegistryItemResponse = element(
        tag='searchRegistryItemResponse', ns=NS_APP
    )


class Envelope(
    BaseXmlModel,
    tag='Envelope',
    ns=NS_SOAP,
    nsmap=NSMAP,
):
    header: Header = element(tag='Header', ns=NS_SOAP)
    body: Body = element(tag='Body', ns=NS_SOAP)
