import logging
import re
from datetime import datetime, date, timedelta

from django.utils.translation import gettext_lazy as _
from django import template
from django.db.models import Manager, DecimalField, IntegerField, FloatField, Model
from django.apps import apps
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe

_logger = logging.getLogger(__name__)
register = template.Library()


@register.simple_tag(name='combine_templates')
def combine_templates(template_name, request=None):
    html = ''
    for app in apps.app_configs.keys():
        try:
            html += render_to_string(
                f'{app}/{template_name}',
                request=request
            )
        except template.TemplateDoesNotExist:
            continue
    return mark_safe(html)


@register.filter(name='related_obj_url')
def related_obj_url(param: object, value: str):
    attr = getattr(param, value, False)
    if attr and hasattr(attr, 'get_absolute_url'):
        return attr.get_absolute_url()
    return '#'


@register.filter(name='get_attr')
def get_attr_from_string(param: object, value: str):

    def return_save(attr):
        if isinstance(attr, Manager) and hasattr(attr, 'related_model'):
            return attr.related_model
        if attr is None:
            return None
        if isinstance(attr, str):
            return attr
        return attr

    try:
        attribute = getattr(param, value, False)
    except (AttributeError,):
        _logger.warning(f'Object {param} has no attribute {value}')
        attribute = None
    return return_save(attribute)


@register.filter(name='has_attr')
def has_attr(param: object, value:str):
    return hasattr(param, value)


@register.filter(name='get_item')
def get_item(param, value):
    return param[value]


@register.filter(name='get_model')
def get_model(instance):
    return instance._meta.model


@register.filter(name='verbose_field_name')
def verbose_field_name(param: object, value: str):
    if not param:
        return ''
    if isinstance(param, Model):
        name = param._meta.get_field(value).verbose_name
    else:
        name = getattr(param.model, value).field.verbose_name
    return name


@register.filter(name='table_display')
def table_display(param: object, value: str):
    field = getattr(param, value)
    if isinstance(field, Manager):
        return mark_safe(f"""
            <span>            
                <div class="tags">
                    {''.join(['<span class="tag">{}</span>'.format(o) for o in field.all()])}
                </div>
            </span>
        """)
    return get_attr_from_string(param, value)


@register.filter(name='table_alignment')
def table_alignment(param, value):
    if isinstance(param, str):
        return 'left'
    if isinstance(param, Model):
        field = param._meta.get_field(value)
    else:
        field = getattr(param.model, value).field
    if isinstance(field, (DecimalField, IntegerField, FloatField)):
        return 'right'
    return 'left'


@register.filter(name='message_class')
def message_class(message):
    if message.level == 25:
        return 'is-success'
    if message.level == 30:
        return 'is-warning'
    if message.level == 40:
        return 'is-danger'


@register.filter(name='timedelta_cast')
def timedelta_cast(td: timedelta, code: str) -> str | None:
    if not isinstance(td, timedelta):
        return None
    codes = ['days', 'hours', 'minutes', 'seconds', 'microseconds']
    if code not in codes:
        return None
    return str(getattr(td, code))


@register.filter(name='weekday')
def datetime_to_weekday(dt: datetime|date, default=None) -> str:
    if dt is None:
        return default
    mapping = {
        1: _('Mon'),
        2: _('Tue'),
        3: _('Wed'),
        4: _('Thu'),
        5: _('Fri'),
        6: _('Sat'),
        7: _('Sun')
    }
    return mapping[dt.isoweekday()]

@register.filter(name='xrefsave')
def x_ref_save(param: str):
    return re.sub(r'[^A-Za-z]+','', param)
