import logging

from datetime import datetime
from typing import AsyncGenerator, get_type_hints

from intranet.trip.src.lib.xml_parser.api import file_xml_parser, text_xml_parser
from intranet.trip.src.lib.xml_parser.exceptions import XmlParserError


logger = logging.getLogger(__name__)


class Parser:
    def __init__(self, filename: str = None, raw_xml: str = None):
        assert filename or raw_xml
        self._filename = filename
        self._raw_xml = raw_xml.strip()

    async def parser(self) -> AsyncGenerator:
        if self._filename:
            return file_xml_parser(self._filename)
        return text_xml_parser(self._raw_xml)


class RegistryParser(Parser):
    @staticmethod
    def _get_value(attr_name: str, attr_val: str, attr_type: type):
        if attr_type == int:
            return int(attr_val or '0')
        elif attr_type == float:
            return float(attr_val.replace(',', '.') or '0.0')
        elif attr_name.lower().endswith('_date'):
            if not attr_val:
                return ''
            for date_format in ('%d.%m.%Y %H:%M:%S', '%d.%m.%Y', '%d.%m.%y',):
                try:
                    return datetime.strptime(attr_val, date_format).strftime('%Y-%m-%d')
                except ValueError:
                    continue
            logger.warning('Unexpected date format %s', attr_val)
            return attr_val
        else:
            return attr_val or ''

    @staticmethod
    async def rows_iterator(xml_parsers: list[AsyncGenerator], type_row: type):
        for xml_parser in xml_parsers:
            async for row in xml_parser:
                one = None
                for events, element in row:
                    if element.tag == 'Document':
                        continue
                    if events == 'start' and element.tag == 'StrDoc':
                        one = dict()
                    elif events == 'end' and element.tag == 'StrDoc':
                        yield type_row(**one)
                    elif events == 'start':
                        attr = element.tag.upper()
                        if attr in one:
                            raise XmlParserError(msg='Several equal tags in row')
                        annotations = get_type_hints(type_row)
                        if attr not in annotations:
                            raise XmlParserError(msg='Wrong name for option: {}'.format(attr))
                        one[attr] = RegistryParser._get_value(
                            attr_name=attr,
                            attr_val=element.text,
                            attr_type=annotations[attr],
                        )

    async def execute(self, func, type_row, *args, **kwargs):
        parser = await self.parser()
        async for row in self.rows_iterator(parser, type_row):
            await func(row, *args, **kwargs)
