import re

from dateutil.relativedelta import relativedelta
from django.core import exceptions
from django.core.validators import RegexValidator
from django.db.models import CharField, Field
from django.utils.translation import gettext_lazy as _
from typing import Union, Optional


startrek_issue_key_validator = RegexValidator(
    regex=r'^[A-Z]+-\d+$',
    code='incorrect_key_format',
    message='Некорректный формат тикета',
)


class StartrekIssueKeyField(CharField):

    def __init__(self, *args, **kwargs):
        kwargs['max_length'] = 32
        super().__init__(*args, **kwargs)
        self.validators.append(startrek_issue_key_validator)

    def to_python(self, value):
        value = super().to_python(value)
        value = value or ''

        # Если вдруг прислали ссылку, обрезаем от последнего слеша
        value = value[value.rfind('/') + 1:]
        value = value.strip().upper()

        return value


postgres_units = {
    'years': 'years',
    'months': 'mons',
    'days': 'days',
    'hours': 'hours',
    'minutes': 'minutes',
    'seconds': 'seconds',
    'microseconds': 'microseconds'
}


postgres_interval_re = re.compile(
    r'^' +
    "".join([
        f'(?:(?P<{key}>-?\\d+) ({unit}? ?))?'
        for key, unit in postgres_units.items()
    ]) +
    r'$'
)


def parse_relative_delta(value: str) -> relativedelta:
    match = postgres_interval_re.match(value)
    if match:
        kw = match.groupdict()
        for key, value in list(kw.items()):
            if value is None:
                kw.pop(key)
            else:
                kw[key] = int(kw[key])
        return relativedelta(**kw)


def format_relative_delta(value: relativedelta) -> str:
    alive_deltas = []
    for key, unit in postgres_units.items():
        delta = getattr(value, key)
        if delta is not None and delta != 0:
            alive_deltas.append(f'{delta} {unit}')
    return " ".join(alive_deltas)


class RelativeDeltaField(Field):
    """
    Postresql Interval + relativedelta.
    Поддержан только Postgres
    """
    default_error_messages = {
        'invalid': _("'%(value)s' value has an invalid format. It must be in "
                     "the traditional Postgres format.")
    }
    description = _("RelativeDelta")
    help_text = _(
        'Postgresql interval field. Using the traditional Postgres format, '
        'e.g: "1 years 5 mons 20 days", "3 mons", etc'
    )

    def get_internal_type(self):
        return "RelativeDeltaField"

    def to_python(self, value: Optional[Union[str, relativedelta]]):
        if value is None:
            return value
        if isinstance(value, relativedelta):
            return value
        try:
            parsed = parse_relative_delta(value)
        except ValueError:
            pass
        else:
            if parsed is not None:
                return parsed

        raise exceptions.ValidationError(
            self.error_messages['invalid'],
            code='invalid',
            params={'value': value},
        )

    def get_db_prep_value(self, value, connection, prepared=False):
        if connection.vendor == 'postgresql':
            if isinstance(value, relativedelta):
                return format_relative_delta(value)
            else:
                return value
        else:
            raise exceptions.FieldError('DB is not supported')

    def get_db_converters(self, connection):
        if connection.vendor != 'postgresql':
            raise exceptions.FieldError('DB is not supported')

        def parser(value, _connection, _expression):
            if isinstance(value, str):
                return parse_relative_delta(value)
            else:
                return value

        return [parser]

    def db_type(self, connection):
        return "interval"

    def value_to_string(self, obj):
        val = self.value_from_object(obj)
        return '' if val is None else format_relative_delta(val)

    def formfield(self, **kwargs):
        from intranet.femida.src.api.core.fields import RelativeDeltaField

        return super().formfield(**{
            'form_class': RelativeDeltaField,
            **kwargs,
        })

    def select_format(self, compiler, sql, params):
        if compiler.connection.vendor == 'postgresql':
            fmt_str = 'YYYY "years" MM "mons" DD "days" HH24 "hours" MI "minutes" SS "seconds" US "microseconds"'
            fmt = f'to_char({sql}, \'{fmt_str}\')'
        else:
            raise exceptions.FieldError('DB is not supported')
        return fmt, params
