import ast
import json
from dataclasses import dataclass

from django.http import HttpResponse
from django.template.loader import render_to_string


class Response:

    def __init__(self, *, template: str, context: dict):
        self.template = template
        self.context = context

    def add_trigger(self, response):
        pass

    def get_context(self):
        return self.context

    def render(self, request) -> str:
        return render_to_string(
            template_name=self.template, context=self.get_context(), request=request
        )

    def response(self, request, extra_content: str = None, replace_body: bool = False) -> HttpResponse:
        extra_content = extra_content or ''
        res = HttpResponse(content=(
            self.render(request)
            + render_to_string('ui/message.html', request=request)
            + extra_content
        ))
        self.add_trigger(res)
        if replace_body:
            res.headers['HX-Retarget'] = 'body'
            res.headers['HX-Reswap'] = 'innerHTML'
            res.headers['HX-Push-Url'] = request.path
        return res


class OobResponse(Response):

    oob_template = 'ui/oob.html'

    def __init__(
        self, *,
        template: str,
        context: dict,
        swap: str = '',
        tag: str = 'div',
        wrap: bool = True,
        attrs: dict = None
    ):
        super().__init__(template=self.oob_template, context=context)
        self.include_template = template
        self.swap = swap
        self.tag = tag
        self.wrap = wrap
        self.attrs = attrs or {}

    def get_context(self):
        context = super().get_context()
        context.update({'oob': {
            'template': self.include_template,
            'swap': self.swap,
            'tag': self.tag,
            'wrap': self.wrap,
            'attrs': self.attrs
        }})
        return context


@dataclass
class ClientTrigger:

    trigger: dict | str
    header: str = 'HX-Trigger'


class TriggerResponse:

    def __init__(self, trigger: list[ClientTrigger]):
        self.trigger = trigger

    def response(self):
        res = HttpResponse()
        res.headers['HX-Reswap'] = 'none'
        for trigger in self.trigger:
            add_trigger(res, trigger.trigger, trigger.header)
        return res


def search_select_response(queryset) -> HttpResponse:
    return HttpResponse(render_to_string(
        'ui/widgets/model_search_select_options.html',
        {'options': queryset}
    ))


def message_response(request, persistent: bool = False):
    return HttpResponse(content=(render_to_string(
        'ui/message.html', context={'persistent': persistent}, request=request
    )))


def update(request, responses: list[Response], trigger: list[ClientTrigger] = None) -> HttpResponse:
    response = HttpResponse()
    content = ''
    for res in responses:
        content += res.render(request)
        res.add_trigger(response)
    content += render_to_string('ui/message.html', request=request)
    response.content = content
    for t in trigger or []:
        add_trigger(response, t.trigger, t.header)
    return response


def redirect_response(url: str, reload: bool = True):
    res = HttpResponse()
    header = 'HX-Redirect' if reload else 'HX-Location'
    res[header] = url
    return res


def add_trigger(
    response: HttpResponse,
    trigger: dict | str,
    header: str = 'HX-Trigger'
) -> HttpResponse:
    if isinstance(trigger, str):
        trigger = {trigger: ''}
    res_trigger = response.headers.get(header)
    if not res_trigger:
        response.headers[header] = json.dumps(trigger)
        return response
    try:
        res_trigger = ast.literal_eval(response.headers.get(header, '{}'))
    except SyntaxError:
        res_trigger = {response.headers[header]: ''}
    res_trigger.update(trigger)
    response.headers[header] = json.dumps(res_trigger)
    return response
