from django.core import paginator
from django.db import models
from django.http import HttpResponse
from django.utils.translation import gettext_lazy as _
from django.utils.functional import Promise
from django.utils.safestring import mark_safe
from accrete.contrib.ui import Filter
from .response import Response, OobResponse
from .window import WindowResponse, WindowResponseConfig


class TableResponse(WindowResponse):

    def __init__(
        self, *,
        title: str | Promise,
        context: dict,
        fields: list[str],
        base_template: str = None,
        instance_label: str | Promise | None = None,
        active_instance: models.Model = None,
        footer: dict = None,
        page: paginator.Page = None,
        ui_filter: Filter = None,
        endless_scroll: bool = False,
        header_template: str = None,
        panel_template: str = None,
        overview_template: str = 'ui/table.html',
        detail_header_template: str = None,
        detail_data_template: str = None,
        detail_enabled: bool = True,
        can_compact: bool = True,
        config: WindowResponseConfig = None
    ):
        assert page is not None or ui_filter is not None, _(
            'Argument page or ui_filter must be supplied'
        )
        super().__init__(
            title=title,
            context=context,
            overview_template=overview_template,
            header_template=header_template,
            panel_template=panel_template,
            config=config
        )
        self.instance_label = instance_label
        self.active_instance = active_instance
        self.fields = fields
        self.base_template = base_template or self.base_template
        self.footer = footer or {}
        self.page = page or (ui_filter and ui_filter.get_page())
        self.ui_filter = ui_filter
        self.endless_scroll = endless_scroll
        self.detail_header_template = detail_header_template
        self.detail_data_template = detail_data_template
        self.detail_enabled = detail_enabled
        self.can_compact = can_compact

    def _has_panel(self):
        return bool(self.panel_template or self.ui_filter)

    def get_context(self):
        context = super().get_context()
        has_url = hasattr(self.page.object_list.first(), 'get_absolute_url')
        context.update({
            'page': self.page,
            'ui_filter': self.ui_filter,
            'endless_scroll': self.endless_scroll,
            'fields': self.fields,
            'instance_label': self.instance_label,
            'active_instance': self.active_instance,
            'table_data': self.table_data(),
            'footer': self.footer,
            'detail_header_template': self.detail_header_template,
            'detail_data_template': self.detail_data_template,
            'detail_enabled': self.detail_enabled,
            'can_compact': self.can_compact,
            'has_url': has_url,
            'show_content_right': str(bool(
                (self.active_instance and has_url and self.detail_enabled)
                or (self.detail_header_template or self.detail_data_template)
            )).lower()
        })
        return context

    def table_data(self):
        data = {
            'header': {'fields': []},
            'body': {'rows': []},
            'footer': {'fields': []},
        }
        meta_data = self.table_meta_data(self.page.object_list.model)
        if self.instance_label:
            data['header']['fields'].insert(0, {
                'content': self.instance_label, 'alignment': 'left'
            })
            data['footer']['fields'].insert(0, {
                'content': '', 'alignment': 'left'})
        for field_name in self.fields:
            alignment = meta_data.get(field_name, {}).get('alignment', 'left')
            verbose_name = meta_data.get(field_name, {}).get('verbose_name', '')
            data['header']['fields'].append({
                'content': verbose_name,
                'alignment': alignment,
            })
            data['footer']['fields'].append({
                'content': self.footer.get(field_name, ''),
                'verbose_name': verbose_name,
                'alignment': alignment,
            })
        for instance in self.page.object_list:
            row_data = {
                'fields': [],
                'id': instance.pk,
                'is_active': bool(instance == self.active_instance),
                'hx_get': self.detail_enabled and self.table_row_url(instance) or False,
            }
            if self.instance_label:
                row_data['fields'].insert(0, {'content': str(instance)})
            for field_name in self.fields:
                alignment = meta_data.get(field_name, {}).get('alignment', 'left')
                verbose_name = meta_data.get(field_name, {}).get('verbose_name', '')
                row_data['fields'].append({
                    'content': self.table_field_content(instance, field_name),
                    'verbose_name': verbose_name,
                    'alignment': alignment,
                })
            data['body']['rows'].append(row_data)
        return data

    def table_meta_data(self, model) -> dict:
        return table_meta_data(model)

    def table_row_url(self, instance) -> str:
        return table_row_url(instance)

    def table_field_content(self, instance, field_name) -> str:
        return table_field_content(instance, field_name)

    def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
        return super().response(request, extra_content, replace_body)


class TableRowResponse(Response):

    base_template = 'ui/table_row_update.html'

    def __init__(
        self, *,
        instance: models.Model,
        fields: list[str],
        instance_label: str | Promise | None = None,
        footer: dict = None,
        page: paginator.Page = None,
    ):
        super().__init__(template=self.base_template, context={})
        self.instance = instance
        self.instance.refresh_from_db()
        self.fields = fields
        self.instance_label = instance_label
        self.footer = footer or {}
        self.page = page

    def get_context(self):
        context = super().get_context()
        context.update({
            'instance': self.instance,
            'table_data': self.table_data(),
            'fields': self.fields,
            'instance_label': self.instance_label,
            'footer': self.footer,
            'page': self.page
        })
        return context

    def table_data(self):
        data = {
            'row': [],
            'footer': [],
        }
        data = {
            'header': {'fields': []},
            'body': {'rows': []},
            'footer': {'fields': []},
        }
        meta_data = self.table_meta_data(self.instance._meta.model)
        row_data = {
            'fields': [],
            'id': self.instance.pk,
            'is_active': None,
            'hx_get': None,
        }
        if self.instance_label:
            row_data['fields'].append({'content': str(self.instance), 'alignment': 'left'})
            data['footer']['fields'].append({'content': '', 'alignment': 'left'})
        for field_name in self.fields:
            alignment = meta_data.get(field_name, {}).get('alignment', 'left')
            verbose_name = meta_data.get(field_name, {}).get('verbose_name', '')
            row_data['fields'].append({
                'content': self.table_field_content(self.instance, field_name),
                'verbose_name': verbose_name,
                'alignment': alignment,
            })
            data['footer']['fields'].append({
                'content': self.footer.get(field_name, ''),
                'verbose_name': verbose_name,
                'alignment': alignment,
            })
        data['body']['rows'].append(row_data)
        return data

    def table_meta_data(self, model) -> dict:
        return table_meta_data(model)

    def table_field_content(self, instance, field_name) -> str:
        return table_field_content(instance, field_name)

    def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
        return super().response(request, extra_content, replace_body)

    def render(self, request) -> str:
        res = super().render(request)
        if self.page:
            pagination_update = OobResponse(
                template='ui/layout.html#pagination',
                swap='innerHTML:#pagination',
                context=dict(page=self.page)
            ).render(request)
            res += pagination_update
        return res


def table_meta_data(model: models.Model):
    data = {}
    model_fields = model._meta.get_fields()
    number_fields = [
        'IntegerField',
        'FloatField',
        'DecimalField',
    ]
    for field in filter(lambda f: f.concrete, model_fields):
        data[field.name] = {
            'verbose_name': field.verbose_name,
            'alignment': field.get_internal_type() in number_fields and 'right' or 'left',
        }
    if hasattr(model, 'ui_table_data'):
        data.update(model.ui_table_data())
    return data


def table_row_url(instance) -> str:
    if not hasattr(instance, 'get_absolute_url'):
        return ''
    return instance.get_absolute_url()


def table_field_content(instance, field_name) -> str:
    field = getattr(instance, field_name)
    if isinstance(field, models.Manager):
        return mark_safe(f"""
            <span>
                <div class="tags">
                    {''.join(['<span class="tag">{}</span>'.format(o) for o in field.all()])}
                </div>
            </span>
        """)
    if getattr(field, 'choices', []):
        return getattr(instance, f'get_{field_name}_display')
    return field is not None and field or ''
