"""
Views for Contract/E-Signature system.
"""
import hashlib
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.db import connection
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny, BasePermission

from .models import ContractTemplate, ServiceContractRequirement, Contract, ContractSignature


class HasContractsPermission(BasePermission):
    """
    Permission class to check if the tenant has contracts feature enabled.
    """
    message = "Contracts feature is not enabled for your subscription plan."

    def has_permission(self, request, view):
        user = request.user
        if not user or not user.is_authenticated:
            return False

        # Platform users (superuser, platform_manager, etc.) can access
        if hasattr(user, 'role') and user.role in ['superuser', 'platform_manager', 'platform_support']:
            return True

        # Get tenant from user
        tenant = getattr(user, 'tenant', None)
        if not tenant:
            return False

        # Check if tenant's subscription plan has contracts enabled
        subscription_plan = getattr(tenant, 'subscription_plan', None)
        if subscription_plan:
            return getattr(subscription_plan, 'contracts_enabled', False)

        return False
from .serializers import (
    ContractTemplateSerializer, ContractTemplateListSerializer,
    ServiceContractRequirementSerializer, ContractSerializer, ContractListSerializer,
    PublicContractSerializer, ContractSignatureInputSerializer, CreateContractSerializer
)


def get_client_ip(request):
    """Extract client IP from 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 ContractTemplateViewSet(viewsets.ModelViewSet):
    """
    CRUD for contract templates.
    Permissions: owner/manager only, must have contracts feature enabled
    """
    queryset = ContractTemplate.objects.all()
    permission_classes = [IsAuthenticated, HasContractsPermission]

    def get_serializer_class(self):
        if self.action == "list":
            return ContractTemplateListSerializer
        return ContractTemplateSerializer

    def get_queryset(self):
        qs = super().get_queryset()
        status_filter = self.request.query_params.get("status")
        if status_filter:
            qs = qs.filter(status=status_filter)
        return qs.order_by("name")

    def perform_create(self, serializer):
        serializer.save(created_by=self.request.user)

    @action(detail=True, methods=["post"])
    def duplicate(self, request, pk=None):
        """Create a copy of an existing template"""
        template = self.get_object()
        new_template = ContractTemplate.objects.create(
            name=f"{template.name} (Copy)",
            description=template.description,
            content=template.content,
            scope=template.scope,
            status=ContractTemplate.Status.DRAFT,
            expires_after_days=template.expires_after_days,
            created_by=request.user,
        )
        serializer = ContractTemplateSerializer(new_template)
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @action(detail=True, methods=["post"])
    def new_version(self, request, pk=None):
        """Create a new version of the template"""
        template = self.get_object()
        template.version += 1
        template.version_notes = request.data.get("version_notes", "")
        if "content" in request.data:
            template.content = request.data["content"]
        if "name" in request.data:
            template.name = request.data["name"]
        if "description" in request.data:
            template.description = request.data["description"]
        template.save()
        serializer = ContractTemplateSerializer(template)
        return Response(serializer.data)

    @action(detail=True, methods=["post"])
    def activate(self, request, pk=None):
        """Activate a draft template"""
        template = self.get_object()
        template.status = ContractTemplate.Status.ACTIVE
        template.save(update_fields=["status", "updated_at"])
        return Response({"success": True})

    @action(detail=True, methods=["post"])
    def archive(self, request, pk=None):
        """Archive a template"""
        template = self.get_object()
        template.status = ContractTemplate.Status.ARCHIVED
        template.save(update_fields=["status", "updated_at"])
        return Response({"success": True})

    @action(detail=True, methods=["get"])
    def preview_pdf(self, request, pk=None):
        """Generate a PDF preview of the template"""
        import logging
        logger = logging.getLogger(__name__)

        from django.http import HttpResponse
        from .pdf_service import ContractPDFService, WEASYPRINT_AVAILABLE

        if not WEASYPRINT_AVAILABLE:
            return Response(
                {"error": "PDF generation not available"},
                status=status.HTTP_503_SERVICE_UNAVAILABLE
            )

        template = self.get_object()

        try:
            pdf_bytes = ContractPDFService.generate_template_preview(template, request.user)
            response = HttpResponse(pdf_bytes, content_type="application/pdf")
            response["Content-Disposition"] = f'inline; filename="{template.name}_preview.pdf"'
            return response
        except Exception as e:
            import traceback
            logger.error(f"PDF preview error: {e}")
            logger.error(traceback.format_exc())
            return Response(
                {"error": str(e)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class ServiceContractRequirementViewSet(viewsets.ModelViewSet):
    """Manage which contracts are required for which services"""
    queryset = ServiceContractRequirement.objects.all()
    serializer_class = ServiceContractRequirementSerializer
    permission_classes = [IsAuthenticated, HasContractsPermission]

    def get_queryset(self):
        qs = super().get_queryset().select_related("service", "template")
        service_id = self.request.query_params.get("service")
        template_id = self.request.query_params.get("template")
        if service_id:
            qs = qs.filter(service_id=service_id)
        if template_id:
            qs = qs.filter(template_id=template_id)
        return qs


class ContractViewSet(viewsets.ModelViewSet):
    """
    CRUD for contract instances.
    Includes sending, viewing, and PDF download.
    """
    queryset = Contract.objects.all()
    permission_classes = [IsAuthenticated, HasContractsPermission]

    def get_serializer_class(self):
        if self.action == "list":
            return ContractListSerializer
        if self.action == "create":
            return CreateContractSerializer
        return ContractSerializer

    def get_queryset(self):
        qs = super().get_queryset().select_related(
            "customer", "template", "signature", "event"
        )
        customer_id = self.request.query_params.get("customer")
        status_filter = self.request.query_params.get("status")
        template_id = self.request.query_params.get("template")

        if customer_id:
            qs = qs.filter(customer_id=customer_id)
        if status_filter:
            qs = qs.filter(status=status_filter)
        if template_id:
            qs = qs.filter(template_id=template_id)

        return qs.order_by("-created_at")

    def create(self, request, *args, **kwargs):
        """Create a contract from a template"""
        serializer = CreateContractSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        template = serializer.validated_data["template"]
        customer = serializer.validated_data["customer"]
        event = serializer.validated_data.get("event")
        send_email = serializer.validated_data.get("send_email", True)

        # Render content with variables
        content_html = self._render_template(template, customer, event)
        content_hash = hashlib.sha256(content_html.encode()).hexdigest()

        # Calculate expiration
        expires_at = None
        if template.expires_after_days:
            expires_at = timezone.now() + timezone.timedelta(days=template.expires_after_days)

        contract = Contract.objects.create(
            template=template,
            template_version=template.version,
            title=template.name,
            content_html=content_html,
            content_hash=content_hash,
            customer=customer,
            event=event if template.scope == ContractTemplate.Scope.APPOINTMENT else None,
            expires_at=expires_at,
            sent_by=request.user,
        )

        if send_email:
            from .tasks import send_contract_email
            send_contract_email.delay(contract.id)
            contract.sent_at = timezone.now()
            contract.save(update_fields=["sent_at"])

        response_serializer = ContractSerializer(contract, context={"request": request})
        return Response(response_serializer.data, status=status.HTTP_201_CREATED)

    def _render_template(self, template, customer, event=None):
        """Render template with variable substitution"""
        from smoothschedule.identity.core.models import Tenant

        tenant = Tenant.objects.get(schema_name=connection.schema_name)

        context = {
            "CUSTOMER_NAME": customer.get_full_name() or customer.email,
            "CUSTOMER_FIRST_NAME": customer.first_name or customer.email.split("@")[0],
            "CUSTOMER_LAST_NAME": customer.last_name or "",
            "CUSTOMER_EMAIL": customer.email,
            "CUSTOMER_PHONE": getattr(customer, "phone", "") or "",
            "BUSINESS_NAME": tenant.name,
            "BUSINESS_EMAIL": tenant.contact_email or "",
            "BUSINESS_PHONE": tenant.phone or "",
            "DATE": timezone.now().strftime("%B %d, %Y"),
            "YEAR": timezone.now().strftime("%Y"),
        }

        # Add event-specific variables if available
        if event:
            context["APPOINTMENT_DATE"] = event.start_time.strftime("%B %d, %Y")
            context["APPOINTMENT_TIME"] = event.start_time.strftime("%I:%M %p")
            if event.service:
                context["SERVICE_NAME"] = event.service.name

        content = template.content
        for key, value in context.items():
            content = content.replace(f"{{{{{key}}}}}", str(value or ""))

        return content

    @action(detail=True, methods=["post"])
    def send(self, request, pk=None):
        """Send contract to customer via email"""
        contract = self.get_object()
        if contract.status != Contract.Status.PENDING:
            return Response(
                {"error": "Contract is not pending"},
                status=status.HTTP_400_BAD_REQUEST
            )

        from .tasks import send_contract_email
        send_contract_email.delay(contract.id)

        contract.sent_at = timezone.now()
        contract.save(update_fields=["sent_at"])

        return Response({"success": True, "message": "Contract sent"})

    @action(detail=True, methods=["post"])
    def resend(self, request, pk=None):
        """Resend contract email"""
        contract = self.get_object()
        if contract.status != Contract.Status.PENDING:
            return Response(
                {"error": "Contract is not pending"},
                status=status.HTTP_400_BAD_REQUEST
            )

        from .tasks import send_contract_email
        send_contract_email.delay(contract.id)

        return Response({"success": True, "message": "Contract resent"})

    @action(detail=True, methods=["post"])
    def void(self, request, pk=None):
        """Void a pending contract"""
        contract = self.get_object()
        if contract.status != Contract.Status.PENDING:
            return Response(
                {"error": "Only pending contracts can be voided"},
                status=status.HTTP_400_BAD_REQUEST
            )

        contract.status = Contract.Status.VOIDED
        contract.save(update_fields=["status", "updated_at"])

        return Response({"success": True})

    @action(detail=True, methods=["get"])
    def download_pdf(self, request, pk=None):
        """Download signed contract PDF"""
        from django.http import FileResponse
        from django.core.files.storage import default_storage

        contract = self.get_object()
        if not contract.pdf_path:
            return Response({"error": "PDF not available"}, status=status.HTTP_404_NOT_FOUND)

        try:
            file = default_storage.open(contract.pdf_path, "rb")
            return FileResponse(
                file,
                as_attachment=True,
                filename=f"{contract.title}.pdf"
            )
        except Exception:
            return Response({"error": "PDF not found"}, status=status.HTTP_404_NOT_FOUND)

    @action(detail=True, methods=["get"])
    def export_legal(self, request, pk=None):
        """
        Export a complete legal compliance package for a signed contract.

        Returns a ZIP file containing:
        - signed_contract.pdf - The signed contract document
        - audit_certificate.pdf - Official audit trail certificate
        - signature_record.json - Machine-readable audit data
        - integrity_verification.txt - Hash verification report
        """
        from django.http import HttpResponse
        from .pdf_service import ContractPDFService, WEASYPRINT_AVAILABLE

        contract = self.get_object()

        # Validate contract is signed
        if contract.status != Contract.Status.SIGNED:
            return Response(
                {"error": "Only signed contracts can be exported for legal compliance"},
                status=status.HTTP_400_BAD_REQUEST
            )

        if not hasattr(contract, 'signature') or not contract.signature:
            return Response(
                {"error": "Contract signature data is missing"},
                status=status.HTTP_400_BAD_REQUEST
            )

        if not WEASYPRINT_AVAILABLE:
            return Response(
                {"error": "PDF generation not available on this server"},
                status=status.HTTP_503_SERVICE_UNAVAILABLE
            )

        try:
            zip_buffer = ContractPDFService.generate_legal_export_package(contract)

            # Create filename with customer name and date
            safe_title = "".join(c for c in contract.title if c.isalnum() or c in (' ', '-', '_')).strip()
            safe_customer = "".join(c for c in (contract.customer.get_full_name() or contract.customer.email) if c.isalnum() or c in (' ', '-', '_')).strip()
            signed_date = contract.signature.signed_at.strftime("%Y%m%d")
            filename = f"legal_export_{safe_title}_{safe_customer}_{signed_date}.zip"

            response = HttpResponse(zip_buffer.read(), content_type="application/zip")
            response["Content-Disposition"] = f'attachment; filename="{filename}"'
            return response

        except ValueError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Failed to generate legal export: {e}")
            return Response(
                {"error": "Failed to generate legal export package"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )


class PublicContractSigningView(APIView):
    """
    Public endpoint for signing contracts (no auth required).
    Uses token-based access.
    """
    permission_classes = [AllowAny]

    def get(self, request, token):
        """Get contract details for signing page"""
        contract = get_object_or_404(Contract, signing_token=token)

        # For signed contracts, return the data so the signing page can show the signed view
        if contract.status == Contract.Status.SIGNED:
            serializer = PublicContractSerializer(contract)
            return Response(serializer.data)

        if contract.status == Contract.Status.VOIDED:
            return Response(
                {"error": "Contract has been voided", "status": "voided"},
                status=status.HTTP_400_BAD_REQUEST
            )
        if contract.expires_at and timezone.now() > contract.expires_at:
            contract.status = Contract.Status.EXPIRED
            contract.save(update_fields=["status"])
            return Response(
                {"error": "Contract has expired", "status": "expired"},
                status=status.HTTP_400_BAD_REQUEST
            )

        serializer = PublicContractSerializer(contract)
        return Response(serializer.data)

    def post(self, request, token):
        """Sign the contract"""
        contract = get_object_or_404(Contract, signing_token=token)

        # Validate status
        if contract.status != Contract.Status.PENDING:
            return Response(
                {"error": "Contract cannot be signed", "status": contract.status},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Validate expiration
        if contract.expires_at and timezone.now() > contract.expires_at:
            contract.status = Contract.Status.EXPIRED
            contract.save(update_fields=["status"])
            return Response({"error": "Contract has expired"}, status=status.HTTP_400_BAD_REQUEST)

        serializer = ContractSignatureInputSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # Validate consent
        if not serializer.validated_data["consent_checkbox_checked"]:
            return Response(
                {"error": "You must check the consent box"},
                status=status.HTTP_400_BAD_REQUEST
            )
        if not serializer.validated_data["electronic_consent_given"]:
            return Response(
                {"error": "You must consent to electronic records"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # Standard consent text
        consent_text = (
            "I have read and agree to the terms and conditions outlined in this document. "
            "By checking this box, I understand that this constitutes a legal electronic signature "
            "under the ESIGN Act (15 U.S.C. section 7001 et seq.) and UETA."
        )
        electronic_consent_text = (
            "I consent to conduct business electronically. I understand that: "
            "1. I am agreeing to use electronic records and signatures in place of paper. "
            "2. I have the right to receive documents in paper form upon request. "
            "3. I can withdraw this consent at any time. "
            "4. I need internet access to access these documents. "
            "5. I can request a paper copy at any time."
        )

        # Create signature with audit trail
        signature = ContractSignature.objects.create(
            contract=contract,
            consent_checkbox_checked=True,
            consent_text=consent_text,
            electronic_consent_given=True,
            electronic_consent_text=electronic_consent_text,
            signer_name=serializer.validated_data["signer_name"],
            signer_email=contract.customer.email,
            signed_at=timezone.now(),
            ip_address=get_client_ip(request),
            user_agent=request.META.get("HTTP_USER_AGENT", "")[:500],
            document_hash_at_signing=contract.content_hash,
            latitude=serializer.validated_data.get("latitude"),
            longitude=serializer.validated_data.get("longitude"),
        )

        # Update contract status
        contract.status = Contract.Status.SIGNED
        contract.save(update_fields=["status", "updated_at"])

        # Generate PDF and send confirmation emails asynchronously
        from .tasks import generate_contract_pdf, send_contract_signed_emails
        generate_contract_pdf.delay(contract.id)
        send_contract_signed_emails.delay(contract.id)

        return Response({"success": True, "message": "Contract signed successfully"})
