# -*- coding: utf-8 -*-
from __future__ import annotations

import logging
import uuid

from django.contrib import messages
from django.contrib.auth.views import redirect_to_login
import django.db
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import redirect, render
from django.templatetags.static import static
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.views.generic import FormView, View
from django_htmx.http import trigger_client_event

from hotpocket_backend.apps.accounts.decorators import account_required
from hotpocket_backend.apps.accounts.mixins import AccountRequiredMixin
from hotpocket_backend.apps.htmx import messages as htmx_messages
from hotpocket_backend.apps.ui.constants import StarUnstarAssociationViewMode
from hotpocket_backend.apps.ui.dto.saves import BrowseParams
from hotpocket_backend.apps.ui.forms.associations import (
    ArchiveForm,
    DeleteForm,
    EditForm,
    RefreshForm,
)
from hotpocket_backend.apps.ui.services import UIAssociationsService
from hotpocket_common.constants import AssociationsSearchMode
from hotpocket_soa.dto.associations import (
    AssociationOut,
    AssociationsQuery,
    AssociationUpdateIn,
    AssociationWithTargetOut,
)
from hotpocket_soa.services import AssociationsService, SaveProcessorService

LOGGER = logging.getLogger(__name__)


class AssociationMixin:
    ALLOW_ARCHIVED_ASSOCIATION = False

    def get_association(self,
                        with_target: bool = False,
                        ) -> AssociationOut | AssociationWithTargetOut:
        attribute = '_instance'
        if with_target is True:
            attribute = '_instance_with_target'

        if hasattr(self, attribute) is False:
            setattr(
                self,
                attribute,
                UIAssociationsService().get_or_404(
                    account_uuid=self.request.user.pk,  # type: ignore[attr-defined]
                    pk=self.kwargs['pk'],  # type: ignore[attr-defined]
                    with_target=with_target,
                    allow_archived=self.ALLOW_ARCHIVED_ASSOCIATION,
                ),
            )

        return getattr(self, attribute)


class DetailView(AssociationMixin, AccountRequiredMixin, FormView):
    def get_context_data(self, **kwargs) -> dict:
        result = super().get_context_data(**kwargs)

        result.update({
            'association': self.get_association(),
        })

        return result


class ConfirmationView(DetailView):
    def get_initial(self) -> dict:
        result = super().get_initial()

        association: AssociationWithTargetOut = self.get_association(  # type: ignore[assignment]
            with_target=True,
        )

        result.update({
            'canhazconfirm': 'hai',
            'title': association.title,
            'url': association.target.url,
        })

        return result

    def get_success_url(self) -> str:
        return reverse('ui.associations.browse')


@account_required
def index(request: HttpRequest) -> HttpResponse:
    return redirect(reverse('ui.associations.browse'))


@account_required
def browse(request: HttpRequest) -> HttpResponse:
    params = BrowseParams.from_request(request=request)

    associations = AssociationsService().search(
        query=AssociationsQuery.model_validate(dict(
            account_uuid=request.user.pk,
            before=params.before,
            search=params.search or None,
            mode=params.mode,
        )),
        limit=params.limit,
    )

    before: uuid.UUID | None = None
    if len(associations) == params.limit:
        before = associations[-1].pk

    next_url: str | None = None
    if before is not None:
        next_url = reverse('ui.associations.browse', query=[
            ('before', before),
            ('search', params.search or ''),
            ('limit', params.limit),
            ('mode', params.mode.value),
        ])

    context = {
        'associations': associations,
        'params': params,
        'before': before,
        'next_url': next_url,
    }

    if request.htmx:
        response = render(
            request,
            'ui/associations/partials/associations.html',
            context,
        )

        return trigger_client_event(
            response,
            'HotPocket:BrowseSavesView:updateLoadMoreButton',
            {'next_url': next_url},
            after='swap',
        )

    title: str
    match params.mode:
        case AssociationsSearchMode.STARRED:
            title = _('Starred')

        case AssociationsSearchMode.ARCHIVED:
            title = _('Archived')

        case _:
            title = 'HotPocket'

    context['title'] = title

    return render(
        request,
        'ui/associations/browse.html',
        context,
    )


def view(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
    account_uuid: uuid.UUID | None = None
    allow_archived = False

    is_share = request.GET.get('share', None) is not None
    if request.user.is_anonymous is True or request.user.is_active is False:
        if is_share is True:
            account_uuid = None
        else:
            return redirect_to_login(
                reverse('ui.associations.view', args=(pk,)),
            )
    else:
        if is_share is False:
            account_uuid = request.user.pk
            allow_archived = True

    association = UIAssociationsService().get_or_404(
        account_uuid=account_uuid,
        pk=pk,
        with_target=True,
        allow_archived=allow_archived,
    )

    show_controls = True
    if association.archived_at is not None:
        show_controls = show_controls and False

    if request.user.is_anonymous is True:
        show_controls = show_controls and False
    else:
        if is_share is True:
            show_controls = show_controls and False

    share_url = request.build_absolute_uri(
        reverse(
            'ui.associations.view',
            args=(association.pk,),
            query=[
                ('share', 'true'),
            ],
        ),
    )

    og_card_url = request.build_absolute_uri(
        static('ui/img/og-card.png'),
    )

    return render(
        request,
        'ui/associations/view.html',
        {
            'association': association,
            'show_controls': show_controls,
            'share_url': share_url,
            'is_share': is_share,
            'og_card_url': (
                og_card_url
                if is_share is True
                else None
            ),
        },
    )


class EditView(DetailView):
    template_name = 'ui/associations/edit.html'
    form_class = EditForm

    def get_initial(self) -> dict:
        result = super().get_initial()

        association = self.get_association(with_target=True)

        result.update({
            'url': association.target.url,  # type: ignore[attr-defined]
            'target_title': association.get_title(),
            'target_description': association.get_description(),
        })

        return result

    def form_valid(self, form: EditForm) -> HttpResponse:
        with django.db.transaction.atomic():
            updated_association = AssociationsService().update(  # noqa: F841
                association=self.get_association(),
                update=AssociationUpdateIn.model_validate(form.cleaned_data),
            )

        messages.add_message(
            self.request,
            messages.SUCCESS,
            _('The save has been updated!'),
        )

        return super().form_valid(form)

    def get_success_url(self) -> str:
        return reverse(
            'ui.associations.view',
            args=(self.get_association(with_target=True).pk,),
        )


class StarUnstarView(AssociationMixin, AccountRequiredMixin, View):
    mode = StarUnstarAssociationViewMode.STAR

    def get(self, request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
        with django.db.transaction.atomic():
            association = self.get_association()

            match self.mode:
                case StarUnstarAssociationViewMode.STAR:
                    _ = AssociationsService().star(association=association)

                case StarUnstarAssociationViewMode.UNSTAR:
                    _ = AssociationsService().unstar(association=association)

                case _:
                    raise ValueError(f'Unknown mode: {self.mode.value}')

        if request.htmx:
            return render(
                request,
                'ui/associations/partials/association_card.html',
                {
                    'association': self.get_association(with_target=True),
                },
            )

        return redirect('ui.associations.browse')


@account_required
def post_save(request: HttpRequest, pk: uuid.UUID) -> HttpResponse:
    association = UIAssociationsService().get_or_404(
        account_uuid=request.user.pk,
        pk=pk,
        with_target=True,
    )

    return render(
        request,
        'ui/associations/post_save.html',
        {
            'association': association,
        },
    )


class RefreshView(ConfirmationView):
    template_name = 'ui/associations/refresh.html'
    form_class = RefreshForm

    def form_valid(self, form: RefreshForm) -> HttpResponse:
        result = SaveProcessorService().schedule_process_save(
            save=self.get_association(with_target=True).target,  # type: ignore[attr-defined]
        )

        if self.request.htmx:
            response = JsonResponse({
                'status': 'ok',
                'result': result.id,
            })

            htmx_messages.add_htmx_message(
                request=self.request,
                response=response,
                level=htmx_messages.SUCCESS,
                message=_('The save has been refreshed!'),
            )

            return response
        else:
            messages.add_message(
                self.request,
                messages.SUCCESS,
                _('The save has been refreshed!'),
            )

        return super().form_valid(form)


class ArchiveView(ConfirmationView):
    template_name = 'ui/associations/archive.html'
    form_class = ArchiveForm

    def form_valid(self, form: ArchiveView) -> HttpResponse:
        with django.db.transaction.atomic():
            result = AssociationsService().archive(
                association=self.get_association(),
            )

        if self.request.htmx:
            return JsonResponse({
                'status': 'ok',
                'result': result,
            })

        messages.add_message(
            self.request,
            messages.SUCCESS,
            _('The save has been archived.'),
        )

        return super().form_valid(form)


class DeleteView(ConfirmationView):
    template_name = 'ui/associations/delete.html'
    form_class = DeleteForm

    ALLOW_ARCHIVED_ASSOCIATION = True

    def form_valid(self, form: DeleteForm) -> HttpResponse:
        with django.db.transaction.atomic():
            result = AssociationsService().delete(
                association=self.get_association(),
            )

        if self.request.htmx:
            response = JsonResponse({
                'status': 'ok',
                'result': result,
            })

            htmx_messages.add_htmx_message(
                request=self.request,
                response=response,
                level=htmx_messages.SUCCESS,
                message=_('The save has been deleted.'),
            )

            return response

        messages.add_message(
            self.request,
            messages.SUCCESS,
            _('The save has been deleted.'),
        )

        return super().form_valid(form)

    def get_success_url(self) -> str:
        return reverse(
            'ui.associations.browse',
            query={
                'mode': AssociationsSearchMode.ARCHIVED.value,
            },
        )
