from rest_framework import viewsets, status, serializers   # ← added serializers
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny  # ← added AllowAny
from rest_framework.views import APIView
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from django.shortcuts import get_object_or_404
from django.http import HttpResponse
from django.template.loader import render_to_string
from django.conf import settings
from django.contrib.auth.hashers import check_password
from django.utils import timezone
from django.contrib.auth.models import User
from docx import Document           # ← added

import io
import os
import hashlib
from user_agents import parse

# Local imports
from agreements.utils.builders import *
from agreements.utils.pdf_generator import generate_pdf
from agreements.utils.pdf_overlay import overlay_pdf
from agreements.utils.helper import *

from .models import (
    Agency, Client, Employee, Agreement, ScopeNode, ResponsibilityNode,
    TimelinePhase, PaymentSection, PaymentMilestone, AnnexureTemplate,
    Annexure, Terms, AgreementTerms, CustomSection, AgreementAttachment,
    Signature, AgreementHistory, AgreementTemplate, TemplateSection,
    AgreementPageTemplate, AgreementPage
)
from .serializers import (
    AgencySerializer, ClientSerializer, EmployeeSerializer,
    AgreementReadSerializer,AgreementWriteSerializer, ScopeNodeWriteSerializer, ResponsibilityNodeWriteSerializer,
    TimelinePhaseSerializer, PaymentSectionSerializer, PaymentMilestoneSerializer,
    AnnexureSerializer, TermsSerializer, AgreementTermsSerializer,
    CustomSectionSerializer, AgreementAttachmentSerializer,
    SignatureSerializer, AgreementHistorySerializer,
    AgreementTemplateSerializer, TemplateSectionSerializer,
    AgreementPageTemplateSerializer, AgreementPageSerializer,
    AgreementSummarySerializer, Base64ImageField
)


# ======================================================================
# SERIALIZERS for client-facing APIs
# ======================================================================

class ClientAgreementLoginSerializer(serializers.Serializer):
    token = serializers.CharField()
    password = serializers.CharField(style={"input_type": "password"})  # ← added style


class ClientSignSerializer(serializers.Serializer):
    signature = serializers.CharField()
    client_name = serializers.CharField()


class ClientAgreementSerializer(serializers.ModelSerializer):
    """
    Serializer for the client portal.
    """
    agency_name = serializers.CharField(source="agency.company_name")
    principal_name = serializers.CharField(source="principal.company_name")

    rendered_html = serializers.SerializerMethodField()
    client_signature = serializers.SerializerMethodField()
    client_name = serializers.SerializerMethodField()

    class Meta:
        model = Agreement
        fields = [
            "agreement_no",
            "title",
            "purpose",
            "status",
            "agreement_date",
            "version_number",

            "agency_name",
            "principal_name",

            "client_signed",
            "client_signed_at",

            "client_name",
            "client_signature",

            "rendered_html",
        ]

    def get_rendered_html(self, obj):
        context = build_agreement_context(obj)

        # Build the HTML required by agreement.html
        context["agreement_status"] = (
            "SIGNED"
            if obj.client_signed
            else obj.get_status_display()
        )

        context["scope_html"] = build_scope_tree(
            context["scope_tree"]
        )

        context["responsibility_html"] = build_responsibility_tree(
            context["responsibility_tree"]
        )

        context["timeline_html"] = build_timeline_table(
            context["timeline_phases"]
        )

        context["payment_html"] = build_payment_html(
            context["payment"]
        )

        context["terms_html"] = build_terms_html(
            context["terms"]
        )

        context["attachments"] = obj.attachments.all()

        return render_to_string(
            "agreement/agreement.html",
            context
        )

    def get_client_name(self, obj):
        if hasattr(obj, "signature"):
            return obj.signature.client_name
        return None

    def get_client_signature(self, obj):
        if hasattr(obj, "signature") and obj.signature.client_signature:
            request = self.context.get("request")
            if request:
                return request.build_absolute_uri(
                    obj.signature.client_signature.url
                )
        return None


# ======================================================================
# HELPERS (shared)
# ======================================================================
import base64
from django.core.files.base import ContentFile
def build_agreement_context(agreement):
    """Return a dictionary of all data needed to render an agreement."""

    def build_scope_tree(nodes):
        result = []
        for node in nodes:
            result.append({
                'id': node.id,
                'name': node.name,
                'description': node.description,
                'node_type': node.get_node_type_display(),
                'order': node.order,
                'children': build_scope_tree(node.children.all())
            })
        return result

    def build_resp_tree(nodes):
        result = []
        for node in nodes:
            result.append({
                'id': node.id,
                'name': node.name,
                'description': node.description,
                'node_type': node.get_node_type_display(),
                'order': node.order,
                'children': build_resp_tree(node.children.all())
            })
        return result

    import base64
    from io import BytesIO
    from PIL import Image


    def image_to_base64(file_field, mime_type='image/png'):
        if not file_field or not file_field.path:
            return None

        try:
            with Image.open(file_field.path) as img:

                # Always convert to PNG for reliable PDF rendering
                if img.mode in ('RGBA', 'LA', 'P'):
                    img = img.convert('RGBA')
                else:
                    img = img.convert('RGB')

                buffer = BytesIO()

                img.save(
                    buffer,
                    format='PNG'
                )

                data = buffer.getvalue()

            b64 = base64.b64encode(data).decode('utf-8')

            return f'data:image/png;base64,{b64}'

        except Exception as e:
            print(
                "IMAGE TO BASE64 ERROR:",
                file_field.name if file_field else None,
                repr(e)
            )
            return None

    def get_mime_type(file_field):
        if not file_field:
            return 'image/png'
        ext = file_field.name.split('.')[-1].lower()
        return {
            'png': 'image/png',
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'gif': 'image/gif',
            'webp': 'image/webp',
            'svg': 'image/svg+xml',
        }.get(ext, 'image/png')

    # ========== NEW signature handling ==========
    signature_obj = getattr(agreement, 'signature', None)

    if signature_obj:
        sig_data = {
            'agency_name': signature_obj.agency_name or agreement.agency.company_name,
            'agency_designation': signature_obj.agency_designation or '',
            'client_name': signature_obj.client_name or agreement.principal.contact_person,
            'agency_signature_b64': image_to_base64(
                signature_obj.agency_signature,
                get_mime_type(signature_obj.agency_signature)
            ),
            'client_signature_b64': image_to_base64(
                signature_obj.client_signature,
                get_mime_type(signature_obj.client_signature)
            ),
        }
        
    else:
        # Fallback – no Signature record yet
        sig_data = {
            'agency_name': agreement.agency.company_name,
            'agency_designation': '',
            'client_name': agreement.principal.contact_person,
            'agency_signature_b64': None,
            'client_signature_b64': None,
        }
    # ===========================================

    return {
        'agreement': agreement,
        'agency': agreement.agency,
        'principal': agreement.principal,
        'referred_by': agreement.referred_by,
        'scope_tree': build_scope_tree(agreement.scope_nodes.filter(parent__isnull=True)),
        'responsibility_tree': build_resp_tree(agreement.responsibility_nodes.filter(parent__isnull=True)),
        'timeline_phases': agreement.timeline_phases.all(),
        'payment': agreement.payment if hasattr(agreement, 'payment') else None,
        'annexure': agreement.annexure if hasattr(agreement, 'annexure') else None,
        'terms': [link.terms for link in agreement.terms_links.all()],
        'custom_sections': agreement.custom_sections.all(),
        'signature': sig_data,   # always a dict
    }
from pathlib import Path
def generate_agreement_pdf(agreement):
    """
    Generate PDF for a given agreement.
    Returns bytes of the PDF.
    """
    page_template = AgreementPageTemplate.objects.filter(is_default=True).first()
    if not page_template:
        raise ValueError("No default page template found.")

    context = build_agreement_context(agreement)
    context["page_template"] = page_template
    context["agreement_status"] = (
        "SIGNED" if agreement.client_signed else agreement.get_status_display()
    )
    context["scope_html"] = build_scope_tree(context["scope_tree"])
    context["responsibility_html"] = build_responsibility_tree(context["responsibility_tree"])
    context["timeline_html"] = build_timeline_table(context["timeline_phases"])
    context["payment_html"] = build_payment_html(context["payment"])
    context["terms_html"] = build_terms_html(context["terms"])
    context["attachments"] = agreement.attachments.all()

    html_content = render_to_string("agreement/agreement.html", context)

    options = {
        "page-size": "A4",
        "margin-top": "0",
        "margin-right": "0",
        "margin-bottom": "0",
        "margin-left": "0",
        "encoding": "UTF-8",
        "enable-local-file-access": None,
        "print-media-type": None,
        "background": None,
    }

    pdf_data = generate_pdf(html_content)
    template_path = os.path.join(
        settings.MEDIA_ROOT,
        "agreement_templates",
        "website_agreement_template.pdf"
    )
    pdf_data = overlay_pdf(pdf_data, template_path)
    return pdf_data


# ======================================================================
# ADMIN VIEWSETS
# ======================================================================

class AgencyViewSet(viewsets.ModelViewSet):
    queryset = Agency.objects.all()
    serializer_class = AgencySerializer
    permission_classes = [IsAuthenticated]


class ClientViewSet(viewsets.ModelViewSet):
    queryset = Client.objects.all()
    serializer_class = ClientSerializer
    permission_classes = [IsAuthenticated]


class EmployeeViewSet(viewsets.ModelViewSet):
    queryset = Employee.objects.all()
    serializer_class = EmployeeSerializer
    permission_classes = [IsAuthenticated]


class AgreementViewSet(viewsets.ModelViewSet):
    queryset = Agreement.objects.select_related('agency', 'principal', 'referred_by', 'parent_agreement', 'template_used') \
        .prefetch_related(
            'scope_nodes',
            'responsibility_nodes',
            'timeline_phases',
            'payment__milestones',
            'annexure',
            'terms_links__terms',
            'custom_sections',
            'attachments',
            'signature',
            'history'
        )
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['status', 'agreement_date', 'agency', 'principal', 'referred_by', 'is_latest']
    search_fields = ['title', 'agreement_no', 'agency__company_name', 'principal__company_name']
    ordering_fields = ['agreement_date', 'created_at', 'updated_at', 'agreement_no']
    ordering = ['-created_at']


    def get_serializer_class(self):
        if self.action in ["create", "update", "partial_update"]:
            return AgreementWriteSerializer

        return AgreementReadSerializer

    def create(self, request, *args, **kwargs):
        write_serializer = AgreementWriteSerializer(
            data=request.data,
            context=self.get_serializer_context()
        )
        write_serializer.is_valid(raise_exception=True)

        agreement = write_serializer.save()

        read_serializer = AgreementReadSerializer(
            agreement,
            context=self.get_serializer_context()
        )

        headers = self.get_success_headers(read_serializer.data)

        return Response(
            read_serializer.data,
            status=status.HTTP_201_CREATED,
            headers=headers,
        )
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop("partial", False)
        instance = self.get_object()

        write_serializer = AgreementWriteSerializer(
            instance,
            data=request.data,
            partial=partial,
            context=self.get_serializer_context()
        )
        write_serializer.is_valid(raise_exception=True)

        agreement = write_serializer.save()

        read_serializer = AgreementReadSerializer(
            agreement,
            context=self.get_serializer_context()
        )

        return Response(read_serializer.data)
    @action(detail=True, methods=['post'])
    def duplicate(self, request, pk=None):
        original = self.get_object()
        new_title = request.data.get('title', f"Copy of {original.title}")
        new_agreement = clone_agreement(original, new_version=False, new_title=new_title)
        return Response(
            AgreementReadSerializer(new_agreement).data,
            status=status.HTTP_201_CREATED
        )

    @action(detail=True, methods=['post'])
    def new_version(self, request, pk=None):
        original = self.get_object()
        original.is_latest = False
        original.save()
        new_agreement = clone_agreement(original, new_version=True)
        return Response(
            AgreementReadSerializer(new_agreement).data,
            status=status.HTTP_201_CREATED
        )

    @action(detail=True, methods=['patch'])
    def change_status(self, request, pk=None):
        agreement = self.get_object()
        new_status = request.data.get('status')
        if new_status not in dict(Agreement.STATUS_CHOICES):
            return Response({'error': 'Invalid status'}, status=status.HTTP_400_BAD_REQUEST)
        old_status = agreement.status
        agreement.status = new_status
        agreement.save()
        AgreementHistory.objects.create(
            agreement=agreement,
            action='STATUS_CHANGED',
            user=request.user,
            details={'old': old_status, 'new': new_status}
        )
        return Response({'status': agreement.status})

    @action(detail=True, methods=['post'])
    def finalize_payment(self, request, pk=None):
        agreement = self.get_object()
        if hasattr(agreement, 'payment'):
            agreement.payment.calculate_and_save()
            return Response({'detail': 'Payment finalized.'})
        return Response({'error': 'No payment section found.'}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=['get'])
    def preview(self, request, pk=None):
        agreement = self.get_object()
        serializer = AgreementReadSerializer(agreement)
        return Response(serializer.data)

    @action(detail=True, methods=['get'])
    def export_data(self, request, pk=None):
        agreement = self.get_object()
        serializer = AgreementReadSerializer(agreement)
        return Response(serializer.data)

    @action(detail=True, methods=['get'])
    def summary(self, request, pk=None):
        agreement = self.get_object()
        data = {
            'total_scopes': agreement.scope_nodes.filter(parent__isnull=True).count(),
            'total_responsibilities': agreement.responsibility_nodes.filter(parent__isnull=True).count(),
            'total_timeline_phases': agreement.timeline_phases.count(),
            'total_duration': agreement.total_duration,
            'total_amount': agreement.payment.grand_total if hasattr(agreement, 'payment') else 0,
            'payment_milestones_count': agreement.payment.milestones.count() if hasattr(agreement, 'payment') else 0,
            'status': agreement.status,
        }
        serializer = AgreementSummarySerializer(data)
        return Response(serializer.data)

    @action(detail=True, methods=['post'])
    def generate_from_template(self, request, pk=None):
        agreement = self.get_object()
        template_id = request.data.get('template_id')
        if not template_id:
            return Response({'error': 'template_id required'}, status=status.HTTP_400_BAD_REQUEST)

        template = get_object_or_404(AgreementTemplate, id=template_id)
        agreement.template_used = template
        agreement.save()

        for section in template.sections.all():
            section_type = section.section_type
            data = section.data

            if section_type == 'scope':
                self._create_scope_nodes(agreement, data)
            elif section_type == 'responsibility':
                self._create_responsibility_nodes(agreement, data)
            elif section_type == 'timeline':
                for phase_data in data:
                    TimelinePhase.objects.create(agreement=agreement, **phase_data)
            elif section_type == 'payment':
                payment_serializer = PaymentSectionSerializer(data=data)
                payment_serializer.is_valid(raise_exception=True)
                payment_serializer.save(agreement=agreement)
            elif section_type == 'terms':
                for term_data in data:
                    terms = get_object_or_404(Terms, id=term_data.get('terms'))
                    AgreementTerms.objects.create(agreement=agreement, terms=terms, order=term_data.get('order', 0))
            elif section_type == 'custom':
                for custom_data in data:
                    CustomSection.objects.create(agreement=agreement, **custom_data)

        AgreementHistory.objects.create(
            agreement=agreement,
            action='EDITED',
            user=request.user,
            details={'action': 'generated from template'}
        )

        return Response(
            AgreementReadSerializer(agreement).data,
            status=status.HTTP_200_OK
        )

    def _create_scope_nodes(self, agreement, nodes_data, parent=None):
        for node_data in nodes_data:
            children = node_data.pop('children', [])
            node = ScopeNode.objects.create(agreement=agreement, parent=parent, **node_data)
            if children:
                self._create_scope_nodes(agreement, children, parent=node)

    def _create_responsibility_nodes(self, agreement, nodes_data, parent=None):
        for node_data in nodes_data:
            children = node_data.pop('children', [])
            node = ResponsibilityNode.objects.create(agreement=agreement, parent=parent, **node_data)
            if children:
                self._create_responsibility_nodes(agreement, children, parent=node)

    @action(detail=True, methods=['get'])
    def export_pdf(self, request, pk=None):
        agreement = self.get_object()
        try:
            pdf_data = generate_agreement_pdf(agreement)
        except Exception as e:
            return Response(
                {"error": f"PDF generation failed: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR,
            )

        response = HttpResponse(pdf_data, content_type="application/pdf")
        response["Content-Disposition"] = f'attachment; filename="agreement_{agreement.agreement_no}.pdf"'
        response["Cache-Control"] = "no-store"
        return response

    @action(detail=True, methods=['get'])
    def export_docx(self, request, pk=None):
        """
        Export the agreement as a DOCX file using python-docx.
        """
        agreement = self.get_object()
        # context is not used here; removed to avoid unused variable warning
        doc = Document()
        doc.add_heading(f'Agreement: {agreement.title}', level=1)
        doc.add_paragraph(f'Agreement No: {agreement.agreement_no}')
        doc.add_paragraph(f'Date: {agreement.agreement_date}')
        doc.add_paragraph(f'Status: {agreement.get_status_display()}')
        doc.add_paragraph(f'Agency: {agreement.agency.company_name}')
        doc.add_paragraph(f'Principal: {agreement.principal.company_name}')
        doc.add_paragraph(f'Purpose: {agreement.purpose}')

        doc.add_heading('Project Scopes', level=2)
        for scope in agreement.scope_nodes.filter(parent__isnull=True):
            doc.add_paragraph(f'- {scope.get_node_type_display()}: {scope.name}')
            doc.add_paragraph(f'  {scope.description or ""}')
            self._add_scope_children_to_doc(doc, scope, level=1)

        doc.add_heading('Responsibilities', level=2)
        for resp in agreement.responsibility_nodes.filter(parent__isnull=True):
            doc.add_paragraph(f'- {resp.get_node_type_display()}: {resp.name}')
            doc.add_paragraph(f'  {resp.description or ""}')
            self._add_resp_children_to_doc(doc, resp, level=1)

        doc.add_heading('Timeline', level=2)
        for phase in agreement.timeline_phases.all():
            doc.add_paragraph(f'- {phase.phase_name}: Day {phase.start_day} - {phase.end_day}')

        if hasattr(agreement, 'payment'):
            payment = agreement.payment
            doc.add_heading('Payment', level=2)
            doc.add_paragraph(f'Subtotal: {payment.subtotal} {payment.currency}')
            doc.add_paragraph(f'GST: {payment.gst_percent}% - {payment.gst_amount}')
            doc.add_paragraph(f'Discount: {payment.discount_percent}% - {payment.discount_amount}')
            doc.add_paragraph(f'TDS: {payment.tds_percent}% - {payment.tds_amount}')
            doc.add_paragraph(f'Grand Total: {payment.grand_total} {payment.currency}')
            doc.add_paragraph(f'Advance: {payment.advance_amount}')
            doc.add_paragraph(f'Final Payable: {payment.final_payable} {payment.currency}')
            doc.add_heading('Payment Milestones', level=3)
            for milestone in payment.milestones.all():
                doc.add_paragraph(f'- {milestone.milestone_name}: {milestone.percentage}% = {milestone.amount} (due {milestone.due_date})')

        if agreement.terms_links.exists():
            doc.add_heading('Terms & Conditions', level=2)
            for term_link in agreement.terms_links.all():
                doc.add_paragraph(f'- {term_link.terms.name}')

        if hasattr(agreement, 'annexure'):
            doc.add_heading('Annexure', level=2)
            doc.add_paragraph(agreement.annexure.content)

        file_buffer = io.BytesIO()
        doc.save(file_buffer)
        file_buffer.seek(0)

        response = HttpResponse(file_buffer.getvalue(), content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
        response['Content-Disposition'] = f'attachment; filename="agreement_{agreement.agreement_no}.docx"'
        return response

    def _add_scope_children_to_doc(self, doc, node, level):
        for child in node.children.all():
            indent = '    ' * level
            doc.add_paragraph(f'{indent}- {child.get_node_type_display()}: {child.name}')
            if child.description:
                doc.add_paragraph(f'{indent}  {child.description}')
            self._add_scope_children_to_doc(doc, child, level + 1)

    def _add_resp_children_to_doc(self, doc, node, level):
        for child in node.children.all():
            indent = '    ' * level
            doc.add_paragraph(f'{indent}- {child.get_node_type_display()}: {child.name}')
            if child.description:
                doc.add_paragraph(f'{indent}  {child.description}')
            self._add_resp_children_to_doc(doc, child, level + 1)


# ======================================================================
# OTHER ADMIN VIEWSETS (unchanged)
# ======================================================================

class ScopeNodeViewSet(viewsets.ModelViewSet):
    queryset = ScopeNode.objects.all()
    serializer_class = ScopeNodeWriteSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_fields = ['agreement', 'parent', 'node_type']
    ordering_fields = ['order']


class ResponsibilityNodeViewSet(viewsets.ModelViewSet):
    queryset = ResponsibilityNode.objects.all()
    serializer_class = ResponsibilityNodeWriteSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_fields = ['agreement', 'parent', 'node_type']
    ordering_fields = ['order']


class TimelinePhaseViewSet(viewsets.ModelViewSet):
    queryset = TimelinePhase.objects.all()
    serializer_class = TimelinePhaseSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_fields = ['agreement']
    ordering_fields = ['order']


class PaymentSectionViewSet(viewsets.ModelViewSet):
    queryset = PaymentSection.objects.all()
    serializer_class = PaymentSectionSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement']


class PaymentMilestoneViewSet(viewsets.ModelViewSet):
    queryset = PaymentMilestone.objects.all()
    serializer_class = PaymentMilestoneSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_fields = ['payment']
    ordering_fields = ['order']


class AnnexureViewSet(viewsets.ModelViewSet):
    queryset = Annexure.objects.all()
    serializer_class = AnnexureSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement']


class TermsViewSet(viewsets.ModelViewSet):
    queryset = Terms.objects.all()
    serializer_class = TermsSerializer
    permission_classes = [IsAuthenticated]


class AgreementTermsViewSet(viewsets.ModelViewSet):
    queryset = AgreementTerms.objects.all()
    serializer_class = AgreementTermsSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement']


class CustomSectionViewSet(viewsets.ModelViewSet):
    queryset = CustomSection.objects.all()
    serializer_class = CustomSectionSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, OrderingFilter]
    filterset_fields = ['agreement']
    ordering_fields = ['order']


class AgreementAttachmentViewSet(viewsets.ModelViewSet):
    queryset = AgreementAttachment.objects.all()
    serializer_class = AgreementAttachmentSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement']


class SignatureViewSet(viewsets.ModelViewSet):
    queryset = Signature.objects.all()
    serializer_class = SignatureSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement']


class AgreementHistoryViewSet(viewsets.ReadOnlyModelViewSet):
    queryset = AgreementHistory.objects.all()
    serializer_class = AgreementHistorySerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement']


class AgreementTemplateViewSet(viewsets.ModelViewSet):
    queryset = AgreementTemplate.objects.prefetch_related('sections')
    serializer_class = AgreementTemplateSerializer
    permission_classes = [IsAuthenticated]


class TemplateSectionViewSet(viewsets.ModelViewSet):
    queryset = TemplateSection.objects.all()
    serializer_class = TemplateSectionSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['template']


class AgreementPageTemplateViewSet(viewsets.ModelViewSet):
    queryset = AgreementPageTemplate.objects.all()
    serializer_class = AgreementPageTemplateSerializer
    permission_classes = [IsAuthenticated]


class AgreementPageViewSet(viewsets.ModelViewSet):
    queryset = AgreementPage.objects.all()
    serializer_class = AgreementPageSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend]
    filterset_fields = ['agreement', 'page_template']

    @action(detail=False, methods=['post'])
    def render_page(self, request):
        agreement_id = request.data.get("agreement_id")
        page_template_id = request.data.get("page_template_id")

        if not agreement_id or not page_template_id:
            return Response(
                {"error": "agreement_id and page_template_id are required."},
                status=status.HTTP_400_BAD_REQUEST,
            )

        agreement = get_object_or_404(Agreement, id=agreement_id)
        page_template = get_object_or_404(AgreementPageTemplate, id=page_template_id)

        context = build_agreement_context(agreement)
        context["page_template"] = page_template

        html_content = render_to_string("agreement/agreement.html", context)

        page, created = AgreementPage.objects.get_or_create(
            agreement=agreement,
            page_template=page_template,
            defaults={"rendered_html": html_content},
        )

        if not created:
            page.rendered_html = html_content
            page.save(update_fields=["rendered_html"])

        return Response({"id": page.id, "html": html_content}, status=status.HTTP_200_OK)


# ======================================================================
# USER VIEWSET
# ======================================================================

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name']


class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]


# ======================================================================
# CLIENT‑FACING APIS (public, token in URL)
# ======================================================================

class ClientAgreementLoginAPIView(APIView):
    """
    Client logs in with token and password.
    Returns signing token and agreement info.
    """
    permission_classes = [AllowAny]   # ← explicit

    def post(self, request):
        serializer = ClientAgreementLoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        token = serializer.validated_data['token']
        password = serializer.validated_data['password']

        agreement = get_object_or_404(Agreement, client_sign_token=token)

        if not check_password(password, agreement.client_access_password):
            # Log failed login
            AgreementHistory.objects.create(
                agreement=agreement,
                action='LOGIN_FAILED',
                details={'ip': self._get_client_ip(request)}
            )
            return Response(
                {"error": "Invalid password."},
                status=status.HTTP_401_UNAUTHORIZED
            )

        # Mark as viewed only once
        if not agreement.client_viewed:
            ip = self._get_client_ip(request)
            ua = parse(request.META.get('HTTP_USER_AGENT', ''))
            agreement.mark_client_viewed(
                ip=ip,
                browser=ua.browser.family,
                device=ua.device.family
            )

        # Log successful login
        AgreementHistory.objects.create(
            agreement=agreement,
            action='LOGIN_SUCCESS',
            details={
                'ip': self._get_client_ip(request),
                'browser': parse(request.META.get('HTTP_USER_AGENT', '')).browser.family
            }
        )

        # Return full info
        return Response({
            "token": agreement.client_sign_token,
            "agreement_no": agreement.agreement_no,
            "agreement_title": agreement.title,
            "status": agreement.status,
            "client_signed": agreement.client_signed,
            "client_signed_at": agreement.client_signed_at,
            "version": agreement.version_number,
            "client_name": agreement.principal.contact_person,
        })

    def _get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0].strip()
        else:
            ip = request.META.get('REMOTE_ADDR', '')
        return ip


class ClientAgreementAPIView(APIView):
    """
    Retrieve agreement data using the signing token (path param).
    Used by the client review page.
    """
    permission_classes = [AllowAny]

    def get(self, request, token):
        agreement = get_object_or_404(Agreement, client_sign_token=token)
        try:
            serializer = ClientAgreementSerializer(agreement,context={"request": request})

            print("SERIALIZER CREATED")

            data = serializer.data


            return Response(data)

        except Exception as e:
            import traceback
            traceback.print_exc()
            return Response(
                {"error": str(e)},
                status=500
            )


class ClientSignAgreementAPIView(APIView):
    """
    Client submits signature.
    """
    permission_classes = [AllowAny]

    def post(self, request, token):
        
        agreement = get_object_or_404(Agreement, client_sign_token=token)
        print("SIGN REQUEST RECEIVED")
        print("Agreement ID:", agreement.id)
        print("Already signed:", agreement.client_signed)
        print("Current status:", agreement.status)
        if agreement.client_signed:
            return Response(
                {"error": "Agreement already signed."},
                status=status.HTTP_409_CONFLICT
            )

        serializer = ClientSignSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        signature_base64 = serializer.validated_data['signature']
        client_name = serializer.validated_data['client_name']

        # Process signature image
        field = Base64ImageField()
        try:
            image = field.to_internal_value(signature_base64)
        except Exception:
            return Response(
                {"error": "Invalid signature image data."},
                status=status.HTTP_400_BAD_REQUEST
            )

        signature, _ = Signature.objects.get_or_create(agreement=agreement)
        # Save the image
        signature.client_signature.save(f"sig_{agreement.agreement_no}.png", image, save=False)

        # Compute hash from image bytes (not the base64 string)
        image.seek(0)  # ensure we read from start
        signature.signature_hash = hashlib.sha256(image.read()).hexdigest()
        image.seek(0)  # reset for later use if needed

        signature.client_name = client_name
        signature.signed_by_client = True
        signature.signed_date = timezone.now().date()
        signature.ip_address = self._get_client_ip(request)
        ua = parse(request.META.get('HTTP_USER_AGENT', ''))
        signature.browser_info = ua.browser.family
        signature.device_info = ua.device.family
        signature.save()

        # Mark agreement as signed
        agreement.mark_signed()
        agreement.refresh_from_db()

        print("=" * 50)
        print("Agreement ID:", agreement.id)
        print("Token:", agreement.client_sign_token)
        print("client_signed:", agreement.client_signed)
        print("status:", agreement.status)
        print("signed_at:", agreement.client_signed_at)
        print("=" * 50)
        # Ensure viewed flag is True (edge case)
        if not agreement.client_viewed:
            agreement.client_viewed = True
            agreement.save(update_fields=['client_viewed'])

        # Log sign completion
        AgreementHistory.objects.create(
            agreement=agreement,
            action='SIGN_COMPLETED',
            details={
                'ip': signature.ip_address,
                'browser': signature.browser_info
            }
        )

        return Response({"message": "Agreement Signed Successfully"})

    def _get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0].strip()
        else:
            ip = request.META.get('REMOTE_ADDR', '')
        return ip


class ClientDownloadAgreementAPIView(APIView):
    """
    Download signed PDF (if signed).
    """
    permission_classes = [AllowAny]

    def get(self, request, token):
        agreement = get_object_or_404(Agreement, client_sign_token=token)

        if not agreement.client_signed:
            return Response(
                {"error": "Agreement not yet signed."},
                status=status.HTTP_403_FORBIDDEN
            )

        # Log download event
        ip = self._get_client_ip(request)
        ua = parse(request.META.get('HTTP_USER_AGENT', ''))
        AgreementHistory.objects.create(
            agreement=agreement,
            action='DOWNLOAD',
            details={
                'ip': ip,
                'browser': ua.browser.family
            }
        )

        # Generate PDF using the shared utility
        try:
            pdf_data = generate_agreement_pdf(agreement)
        except Exception as e:
            return Response(
                {"error": f"PDF generation failed: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        response = HttpResponse(pdf_data, content_type="application/pdf")
        response["Content-Disposition"] = f'attachment; filename="agreement_{agreement.agreement_no}.pdf"'
        response["Cache-Control"] = "no-store"
        return response

    def _get_client_ip(self, request):
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0].strip()
        else:
            ip = request.META.get('REMOTE_ADDR', '')
        return ip
    
from django.core.mail import send_mail
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

from .models import Agreement


class SendAgreementEmailAPIView(APIView):
    """
    Send agreement signing link to the client's email.
    """

    def post(self, request, agreement_no):
        try:
            agreement = Agreement.objects.get(agreement_no=agreement_no)

            if not agreement.principal.email:
                return Response(
                    {"error": "Client email not found."},
                    status=status.HTTP_400_BAD_REQUEST
                )

            sign_url = f"https://agreement.alphas1.in/client-sign.html?token={agreement.client_sign_token}"

            # --- Branding config ---
            LOGO_URL = "https://redalphas.in/redalpha.png"  # TODO: replace with actual logo URL
            PRIVACY_URL = "https://redalphas.in/privacy-policy"
            TERMS_URL = "https://redalphas.in"
            WHATSAPP_NUMBER = "919883405873"  # country code + number, no + or spaces
            WHATSAPP_LINK = f"https://wa.me/{WHATSAPP_NUMBER}"

            subject = "Agreement Signature Request"

            # Plain-text fallback (for email clients that don't render HTML)
            message = f"""
    Hello {agreement.principal.contact_person},

    Please review and sign your agreement using the link below:

    {sign_url}

    Thank you.

    Regards,
    RedAlphas
    """

            # HTML version
            html_message = f"""
    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{subject}</title>
    </head>
    <body style="margin:0; padding:40px 0; background-color:transparent; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;">
    <table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:transparent; padding:40px 0;">
        <tr>
        <td align="center">
            <table role="presentation" width="480" cellpadding="0" cellspacing="0" style="background-color:#ffffff; border-radius:8px; padding:40px; text-align:center; box-shadow:0 1px 3px rgba(0,0,0,0.08);">

            <!-- Logo -->
            <tr>
                <td align="center" style="padding-bottom:24px;">
                <img src="{LOGO_URL}" alt="RedAlphas" width="140" style="display:block; max-width:140px;">
                </td>
            </tr>

            <!-- Heading -->
            <tr>
                <td align="center" style="padding-bottom:8px;">
                <h2 style="margin:0; font-size:18px; color:#1a1a1a; font-weight:600;">
                    Agreement Signature Request
                </h2>
                </td>
            </tr>

            <!-- Body text -->
            <tr>
                <td align="center" style="padding-bottom:24px;">
                <p style="margin:0 0 8px 0; font-size:14px; color:#4a4a4a; line-height:1.6;">
                    Hello {agreement.principal.contact_person},
                </p>
                <p style="margin:0; font-size:14px; color:#4a4a4a; line-height:1.6;">
                    Please review and sign your agreement using the button below.
                </p>
                </td>
            </tr>

            <!-- CTA Button -->
            <tr>
                <td align="center" style="padding-bottom:16px;">
                <a href="{sign_url}" target="_blank"
                    style="display:inline-block; background-color:transparent; color:red; text-decoration:none;
                            font-size:15px; font-weight:600; padding:12px 32px; border-radius:6px;">
                    Review &amp; Sign Agreement
                </a>
                </td>
            </tr>

            <!-- Note -->
            <tr>
                <td align="center" style="padding-bottom:8px;">
                <p style="margin:0; font-size:12px; color:#8a8a8a;">
                    This link is unique to you. Please do not share it with others.
                </p>
                </td>
            </tr>

            <!-- Divider -->
            <tr>
                <td style="padding:24px 0;">
                <hr style="border:none; border-top:1px solid #eaeaea;">
                </td>
            </tr>

            <!-- Footer -->
            <tr>
                <td align="center">
                <p style="margin:0 0 12px 0; font-size:12px; color:#a0a0a0;">
                    © RedAlphas
                </p>
                <p style="margin:0; font-size:12px;">
                    <a href="{PRIVACY_URL}" style="color:#4a6cf7; text-decoration:none;">Privacy policy</a>
                    &nbsp;&nbsp;·&nbsp;&nbsp;
                    <a href="{TERMS_URL}" style="color:#4a6cf7; text-decoration:none;">Terms of service</a>
                    &nbsp;&nbsp;·&nbsp;&nbsp;
                    <a href="{WHATSAPP_LINK}" style="color:#4a6cf7; text-decoration:none;">Chat with us</a>
                </p>
                </td>
            </tr>

            </table>
        </td>
        </tr>
    </table>
    </body>
    </html>
    """

            send_mail(
                subject,
                message,
                settings.DEFAULT_FROM_EMAIL,
                [agreement.principal.email],
                fail_silently=False,
                html_message=html_message,
            )

            return Response(
                {"message": "Signature request sent successfully."},
                status=status.HTTP_200_OK
            )

        except Agreement.DoesNotExist:
            return Response(
                {"error": "Agreement not found."},
                status=status.HTTP_404_NOT_FOUND
            )