from django import forms
from django.contrib import admin, messages
from django.core import signing
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import IntegrityError
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import path, reverse
from django.utils.html import format_html
from apps.parts.models import SparePart
from apps.service.models import ServiceCase, ServiceRepairAction, ServiceDiagnosticFinding
from . import services as s, queries as q
from .models import ServiceQuotation, QuotationLine, QuotationDecision, CommercialWorkAuthorization


class CreationForm(forms.ModelForm):
    service_case = forms.ModelChoiceField(queryset=ServiceCase.objects.none())

    class Meta:
        model = ServiceQuotation
        fields = ("currency", "customer_note", "internal_note", "valid_until")


class MetadataForm(forms.Form):
    currency = forms.CharField(max_length=3)
    customer_note = forms.CharField(required=False, widget=forms.Textarea, max_length=4000)
    internal_note = forms.CharField(required=False, widget=forms.Textarea, max_length=4000)
    valid_until = forms.DateTimeField(required=False)


class LineForm(forms.Form):
    kind = forms.ChoiceField(choices=QuotationLine.Kind.choices)
    responsibility = forms.ChoiceField(choices=QuotationLine.Responsibility.choices)
    description = forms.CharField(max_length=500)
    quantity = forms.IntegerField(min_value=1, max_value=1000000)
    unit_price = forms.DecimalField(min_value=0, max_digits=14, decimal_places=2)
    discount = forms.DecimalField(min_value=0, max_digits=14, decimal_places=2, initial=0)
    tax_rate = forms.DecimalField(min_value=0, max_value=100, max_digits=5, decimal_places=2, initial=0)
    tax_label = forms.CharField(max_length=100, required=False)
    coverage_reason = forms.CharField(max_length=1000, required=False)
    spare_part = forms.ModelChoiceField(queryset=SparePart.objects.select_related("category"), required=False)
    repair_action = forms.ModelChoiceField(queryset=ServiceRepairAction.objects.none(), required=False)
    finding = forms.ModelChoiceField(queryset=ServiceDiagnosticFinding.objects.none(), required=False)

    def __init__(self, *args, service_case, display_choices=None, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["repair_action"].queryset = ServiceRepairAction.objects.filter(repair_execution__service_case=service_case, is_active=True).select_related("repair_action")
        self.fields["finding"].queryset = ServiceDiagnosticFinding.objects.filter(assessment__service_case=service_case).select_related("fault_diagnosis", "root_cause")
        if display_choices:
            for field, choices in display_choices.items():
                self.fields[field].choices = choices


class DecisionForm(forms.Form):
    outcome = forms.ChoiceField(choices=[("APPROVED", "Approved"), ("REJECTED", "Rejected")])
    channel = forms.ChoiceField(choices=QuotationDecision._meta.get_field("channel").choices)
    recipient_name = forms.CharField(max_length=200)
    reference = forms.CharField(max_length=500)
    note = forms.CharField(required=False, widget=forms.Textarea, max_length=2000)


@admin.register(ServiceQuotation)
class QuotationAdmin(admin.ModelAdmin):
    form = CreationForm
    list_display = ("family", "revision", "status", "is_current", "currency", "grand_total", "customer_pay_total")
    list_filter = ("status", "is_current", "currency")
    actions = None

    def get_queryset(self, request):
        return q.service_quotations(actor=request.user)

    def has_delete_permission(self, request, obj=None):
        return False

    def get_readonly_fields(self, request, obj=None):
        return ("workflow",) + tuple(f.name for f in self.model._meta.concrete_fields) if obj else ()

    def get_fields(self, request, obj=None):
        return self.get_readonly_fields(request,obj) if obj else ("service_case", "currency", "customer_note", "internal_note", "valid_until")

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            return forms.modelform_factory(ServiceQuotation, fields=[])
        base = super().get_form(request,obj,**kwargs)
        class ScopedCreation(base):
            def __init__(self, *args, **kw):
                super().__init__(*args,**kw)
                self.fields["service_case"].queryset = q.quotation_cases(actor=request.user, permission=s.MANAGE).filter(status__in=["DIAGNOSED", "REPAIRING"], quotation_family__isnull=True)
        return ScopedCreation

    def workflow(self, obj):
        return format_html('<a href="{}">Manage quotation and inspect lines</a>', reverse("admin:commercial_quotation_workflow", args=[obj.pk]))

    def save_model(self, request, obj, form, change):
        if change:
            raise ValidationError("Use the signed quotation workflow.")
        current = s.create_service_quotation(actor=request.user, **form.cleaned_data)
        for field in obj._meta.concrete_fields:
            setattr(obj,field.attname,getattr(current,field.attname))
        obj._state = current._state

    def changeform_view(self, request, object_id=None, form_url="", extra_context=None):
        try:
            return super().changeform_view(request,object_id,form_url,extra_context)
        except (ValidationError, IntegrityError):
            self.message_user(request,"Quotation operation rejected; reload and review current state.",messages.ERROR)
            return HttpResponseRedirect(request.path)

    def get_urls(self):
        return [path("<uuid:object_id>/workflow/", self.admin_site.admin_view(self.workflow_view), name="commercial_quotation_workflow")] + super().get_urls()

    def workflow_view(self, request, object_id):
        row = get_object_or_404(q.service_quotations(actor=request.user), pk=object_id)
        if not self.has_view_or_change_permission(request,row):
            raise PermissionDenied
        case = row.family.service_case
        token = [str(row.pk),s.revision(row),str(request.user.pk)]
        LineSet = forms.formset_factory(LineForm, extra=1, can_delete=True, max_num=100, validate_max=True)
        initial = [{field:getattr(line,field) for field in LineForm.base_fields} for line in q.quotation_lines(actor=request.user,quotation=row)]
        choice_form = LineForm(service_case=case)
        display_choices = {field:list(choice_form.fields[field].choices) for field in ("spare_part", "repair_action", "finding")}
        operation = request.POST.get("operation") if request.method == "POST" else None
        metadata = MetadataForm(request.POST if operation=="metadata" else None, initial={f:getattr(row,f) for f in MetadataForm.base_fields})
        lines = LineSet(request.POST if operation=="lines" else None, initial=initial, prefix="lines", form_kwargs={"service_case":case,"display_choices":display_choices})
        decision = DecisionForm(request.POST if operation=="decision" else None)
        error = None
        if request.method == "POST":
            if not self.has_change_permission(request,row):
                raise PermissionDenied
            try:
                if signing.loads(request.POST.get("revision_token",""), salt="commercial-workflow") != token:
                    raise ValidationError("Stale quotation form; reload and review.")
                args = dict(actor=request.user,quotation=row,expected_revision=token[1])
                if operation=="metadata" and metadata.is_valid():
                    result = s.update_service_quotation(**args,**metadata.cleaned_data)
                elif operation=="lines" and lines.is_valid():
                    inputs = [{k:v for k,v in form.cleaned_data.items() if k!="DELETE"} for form in lines if form.cleaned_data and not form.cleaned_data.get("DELETE")]
                    result = s.set_quotation_lines(**args,lines=inputs)
                elif operation=="submit":
                    result = s.submit_service_quotation(**args)
                elif operation=="decision" and decision.is_valid():
                    result = s.record_quotation_decision(**args,**decision.cleaned_data)
                elif operation=="revise":
                    result = s.revise_service_quotation(**args)
                else:
                    raise ValidationError("Review the operation and form errors.")
                self.log_change(request,result,f"Quotation workflow: {operation}")
                return HttpResponseRedirect(reverse("admin:commercial_quotation_workflow",args=[result.pk]))
            except (signing.BadSignature, ValidationError, IntegrityError) as exc:
                error = "; ".join(exc.messages) if isinstance(exc,ValidationError) else "Invalid or conflicting quotation submission. Reload and review."
        return TemplateResponse(request,"admin/commercial/workflow.html",{**self.admin_site.each_context(request),
            "opts":self.model._meta,"title":str(row),"quotation":row,"line_rows":q.quotation_lines(actor=request.user,quotation=row),
            "metadata":metadata,"lines":lines,"decision_form":decision,"revision_token":signing.dumps(token,salt="commercial-workflow"),"error":error},status=400 if error else 200)


class EvidenceAdmin(admin.ModelAdmin):
    actions = None
    def has_add_permission(self, request): return False
    def has_change_permission(self, request, obj=None): return False
    def has_delete_permission(self, request, obj=None): return False
    def get_readonly_fields(self, request, obj=None): return tuple(f.name for f in self.model._meta.concrete_fields)
    def get_queryset(self, request):
        return super().get_queryset(request).filter(quotation_id__in=q.service_quotations(actor=request.user).values("pk")).select_related("quotation__family")


admin.site.register(QuotationDecision,EvidenceAdmin)
admin.site.register(CommercialWorkAuthorization,EvidenceAdmin)

from . import invoice_admin  # noqa: E402,F401
from . import payment_admin  # noqa: E402,F401
