from datetime import timedelta
from decimal import Decimal
from django.contrib.auth import authenticate
from django.core.mail import send_mail
from django.db import transaction
from django.db.models import Count, Sum, Q
from django.contrib.auth.hashers import make_password, check_password
from django.utils import timezone
from rest_framework import status
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from openpyxl import Workbook

from .models import (
    User, Business, BusinessBankDetail, BusinessDetailField, BusinessDocument,
    Investor, InvestorDetailField, InvestorDocument, Scheme, KuberBusinessInvestment,
    Investment, BalanceSheet, LedgerEntry, Payout, Certificate, CertificateAttachment,
    AuditLog, RecurringContribution, PasswordResetOTP, AccountantPermission
)
from .permissions import IsAdmin
from .serializers import (
    UserCreateSerializer, UserOutputSerializer, BusinessSerializer,
    BusinessDocumentSerializer, InvestorSerializer, InvestorDocumentSerializer,
    InvestorDetailFieldSerializer, SchemeSerializer, KuberBusinessInvestmentSerializer,
    InvestmentSerializer, BalanceSheetSerializer, LedgerEntrySerializer, PayoutSerializer,
    CertificateSerializer, CertificateAttachmentSerializer, AuditLogSerializer,
    RecurringContributionSerializer, AccountantPermissionSerializer
)
from .services import (
    audit, create_investment_with_snapshot, generate_daily_ledger,
    maturity_value, premature_value
)


def page_response(request, queryset, serializer_class):
    from rest_framework.pagination import PageNumberPagination
    p = PageNumberPagination()
    p.page_size = 20
    p.page_size_query_param = "page_size"
    p.max_page_size = 100
    page = p.paginate_queryset(queryset, request)
    return p.get_paginated_response(serializer_class(page, many=True, context={"request": request}).data)


class AdminOnlyAPIView(APIView):
    permission_classes = [IsAdmin]


class LoginAPIView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        username = request.data.get("username")
        password = request.data.get("password")
        user = authenticate(username=username, password=password)
        if not user or not user.is_active:
            return Response({"detail": "Invalid credentials."}, status=status.HTTP_401_UNAUTHORIZED)
        refresh = RefreshToken.for_user(user)
        return Response({
            "access": str(refresh.access_token),
            "refresh": str(refresh),
            "user": UserOutputSerializer(user).data,
        })


class ForgotPasswordAPIView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        email = request.data.get("email")
        user = User.objects.filter(email__iexact=email, is_active=True).first()
        # Do not reveal whether an email exists.
        if user:
            import secrets
            otp = f"{secrets.randbelow(1000000):06d}"
            PasswordResetOTP.objects.filter(user=user, used=False).update(used=True)
            PasswordResetOTP.objects.create(
                user=user,
                otp_hash=make_password(otp),
                expires_at=timezone.now() + timedelta(minutes=10),
            )
            send_mail(
                "Kuber Trobe Password Reset OTP",
                f"Your OTP is {otp}. It expires in 10 minutes.",
                None,
                [user.email],
            )
        return Response({"detail": "If the email is registered, an OTP has been sent."})


class ResetPasswordAPIView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        email = request.data.get("email")
        otp = request.data.get("otp")
        new_password = request.data.get("new_password")
        user = User.objects.filter(email__iexact=email, is_active=True).first()
        if not user or not otp or not new_password:
            return Response({"detail": "Invalid reset request."}, status=400)

        reset = PasswordResetOTP.objects.filter(
            user=user, used=False, expires_at__gt=timezone.now()
        ).first()
        if not reset or reset.attempts >= 5 or not check_password(otp, reset.otp_hash):
            if reset:
                reset.attempts += 1
                reset.save(update_fields=["attempts"])
            return Response({"detail": "Invalid or expired OTP."}, status=400)

        user.set_password(new_password)
        user.save(update_fields=["password"])
        reset.used = True
        reset.save(update_fields=["used"])
        return Response({"detail": "Password reset successfully."})


class DashboardAPIView(AdminOnlyAPIView):
    def get(self, request):
        businesses = Business.objects.filter(deleted_at__isnull=True)
        investors = Investor.objects.filter(deleted_at__isnull=True)
        investments = Investment.objects.filter(deleted_at__isnull=True)
        kuber_investments = KuberBusinessInvestment.objects.filter(deleted_at__isnull=True)
        one_time_total = investments.filter(amount__isnull=False).aggregate(v=Sum("amount"))["v"] or Decimal("0")
        platinum_total = RecurringContribution.objects.filter(
            investment__deleted_at__isnull=True,
            investment__scheme__category=Scheme.Categories.PLATINUM,
        ).aggregate(v=Sum("amount"))["v"] or Decimal("0")
        total_investment = one_time_total + platinum_total
        payable = LedgerEntry.objects.filter(
            entry_type=LedgerEntry.EntryTypes.SOFT_DEBIT
        ).aggregate(total=Sum("amount"))["total"] or Decimal("0")
        credits = LedgerEntry.objects.filter(
            entry_type=LedgerEntry.EntryTypes.CREDIT_PROFIT
        ).aggregate(total=Sum("amount"))["total"] or Decimal("0")
        debits = LedgerEntry.objects.filter(
            entry_type=LedgerEntry.EntryTypes.DEBIT
        ).aggregate(total=Sum("amount"))["total"] or Decimal("0")
        soft_debits = payable
        return Response({
            "kpis": {
                "total_businesses": businesses.count(),
                "total_investors": investors.count(),
                "total_investment": total_investment,
                "total_kuber_business_investment": kuber_investments.aggregate(v=Sum("amount"))["v"] or 0,
                "interest_payable_to_investors": payable,
                "accumulated_funding": credits - soft_debits,
                "actual_funds": credits - soft_debits - debits,
            },
            "note": "Charts can be built from the same date-grouped aggregates exposed by reporting endpoints.",
        })


class BusinessListCreateAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def get(self, request):
        qs = Business.objects.filter(deleted_at__isnull=True).annotate(
            investor_count=Count("investments", filter=Q(investments__deleted_at__isnull=True), distinct=True),
        )
        return page_response(request, qs, BusinessSerializer)

    @transaction.atomic
    def post(self, request):
        serializer = BusinessSerializer(data=request.data, context={"request": request})
        serializer.is_valid(raise_exception=True)
        business = serializer.save()
        audit(request.user, "CREATE", "BUSINESS", business, "Business created.")
        return Response(BusinessSerializer(business, context={"request": request}).data, status=201)


class BusinessDetailAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def get_object(self, pk):
        return Business.objects.filter(pk=pk, deleted_at__isnull=True).first()

    def get(self, request, pk):
        obj = self.get_object(pk)
        if not obj:
            return Response({"detail": "Business not found."}, status=404)
        return Response(BusinessSerializer(obj, context={"request": request}).data)

    def put(self, request, pk):
        obj = self.get_object(pk)
        if not obj:
            return Response({"detail": "Business not found."}, status=404)
        serializer = BusinessSerializer(obj, data=request.data, partial=True, context={"request": request})
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "UPDATE", "BUSINESS", obj, "Business updated.")
        return Response(BusinessSerializer(obj, context={"request": request}).data)

    def delete(self, request, pk):
        obj = self.get_object(pk)
        if not obj:
            return Response({"detail": "Business not found."}, status=404)
        obj.soft_delete()
        audit(request.user, "DELETE", "BUSINESS", obj, "Business moved to restore bin.")
        return Response(status=204)


class BusinessDocumentCreateAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser]

    def post(self, request, business_id):
        business = Business.objects.filter(pk=business_id, deleted_at__isnull=True).first()
        if not business:
            return Response({"detail": "Business not found."}, status=404)
        serializer = BusinessDocumentSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        doc = serializer.save(business=business, uploaded_by=request.user)
        audit(request.user, "CREATE", "BUSINESS_DOCUMENT", doc, "Business document uploaded.")
        return Response(BusinessDocumentSerializer(doc).data, status=201)


class InvestorDocumentListAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser]
    def get(self, request, investor_id):
        qs = InvestorDocument.objects.filter(investor_id=investor_id)
        return page_response(request, qs, InvestorDocumentSerializer)

    def post(self, request, investor_id):
        investor = Investor.objects.filter(pk=investor_id, deleted_at__isnull=True).first()
        if not investor:
            return Response({"detail": "Investor not found."}, status=404)
        files = request.FILES.getlist("files")
        section = request.data.get("section", InvestorDocument.Sections.OTHER)
        titles = request.data.getlist("titles") if hasattr(request.data, "getlist") else []
        created = []
        for index, file in enumerate(files):
            title = titles[index] if index < len(titles) else file.name
            created.append(InvestorDocument.objects.create(
                investor=investor, section=section, title=title or file.name,
                file=file, uploaded_by=request.user
            ))
        return Response(InvestorDocumentSerializer(created, many=True).data, status=201)


class BusinessInvestorsAPIView(AdminOnlyAPIView):
    def get(self, request, business_id):
        business = Business.objects.filter(pk=business_id, deleted_at__isnull=True).first()
        if not business:
            return Response({"detail": "Business not found."}, status=404)
        qs = Investor.objects.filter(
            investments__business=business,
            investments__deleted_at__isnull=True,
            deleted_at__isnull=True,
        ).distinct()
        return page_response(request, qs, InvestorSerializer)


class InvestorListCreateAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]
    def get(self, request):
        qs = Investor.objects.filter(deleted_at__isnull=True).annotate(
            business_count=Count("investments__business", filter=Q(investments__deleted_at__isnull=True), distinct=True),
        )
        return page_response(request, qs, InvestorSerializer)

    @transaction.atomic
    def post(self, request):
        serializer = InvestorSerializer(data=request.data, context={"request": request})
        serializer.is_valid(raise_exception=True)
        investor = serializer.save()
        audit(request.user, "CREATE", "INVESTOR", investor, "Investor created.")
        return Response(InvestorSerializer(investor, context={"request": request}).data, status=201)


class InvestorDetailAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]
    def get(self, request, pk):
        investor = Investor.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not investor:
            return Response({"detail": "Investor not found."}, status=404)
        investor.business_count = investor.investments.filter(deleted_at__isnull=True).values("business").distinct().count()
        return Response(InvestorSerializer(investor, context={"request": request}).data)

    def put(self, request, pk):
        investor = Investor.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not investor:
            return Response({"detail": "Investor not found."}, status=404)
        serializer = InvestorSerializer(investor, data=request.data, partial=True, context={"request": request})
        serializer.is_valid(raise_exception=True)
        investor = serializer.save()
        audit(request.user, "UPDATE", "INVESTOR", investor, "Investor updated.")
        return Response(InvestorSerializer(investor, context={"request": request}).data)

    def delete(self, request, pk):
        investor = Investor.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not investor:
            return Response({"detail": "Investor not found."}, status=404)
        investor.soft_delete()
        audit(request.user, "DELETE", "INVESTOR", investor, "Investor moved to restore bin.")
        return Response(status=204)


class InvestorPortfolioAPIView(AdminOnlyAPIView):
    def get(self, request, pk):
        investor = Investor.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not investor:
            return Response({"detail": "Investor not found."}, status=404)
        qs = Investment.objects.filter(investor=investor, deleted_at__isnull=True).select_related("business", "scheme")
        return page_response(request, qs, InvestmentSerializer)


class SchemeListCreateAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = Scheme.objects.filter(deleted_at__isnull=True)
        category = request.query_params.get("category")
        if category:
            qs = qs.filter(category=category.upper())
        return page_response(request, qs, SchemeSerializer)

    def post(self, request):
        serializer = SchemeSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        scheme = serializer.save(created_by=request.user)
        audit(request.user, "CREATE", "SCHEME", scheme, "Scheme created.")
        return Response(SchemeSerializer(scheme).data, status=201)


class SchemeDetailAPIView(AdminOnlyAPIView):
    def put(self, request, pk):
        scheme = Scheme.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not scheme:
            return Response({"detail": "Scheme not found."}, status=404)
        serializer = SchemeSerializer(scheme, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        scheme = serializer.save()
        audit(request.user, "UPDATE", "SCHEME", scheme, "Scheme updated.")
        return Response(SchemeSerializer(scheme).data)

    def delete(self, request, pk):
        scheme = Scheme.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not scheme:
            return Response({"detail": "Scheme not found."}, status=404)
        scheme.soft_delete()
        audit(request.user, "DELETE", "SCHEME", scheme, "Scheme moved to restore bin.")
        return Response(status=204)


class KuberBusinessInvestmentListCreateAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = KuberBusinessInvestment.objects.filter(deleted_at__isnull=True).select_related("business")
        return page_response(request, qs, KuberBusinessInvestmentSerializer)

    def post(self, request):
        serializer = KuberBusinessInvestmentSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save(created_by=request.user)
        audit(request.user, "CREATE", "KUBER_BUSINESS_INVESTMENT", obj, "Kuber investment into business created.")
        return Response(KuberBusinessInvestmentSerializer(obj).data, status=201)


class KuberBusinessInvestmentDetailAPIView(AdminOnlyAPIView):
    def put(self, request, pk):
        obj = KuberBusinessInvestment.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not obj:
            return Response({"detail": "Investment not found."}, status=404)
        serializer = KuberBusinessInvestmentSerializer(obj, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "UPDATE", "KUBER_BUSINESS_INVESTMENT", obj, "Kuber investment updated.")
        return Response(KuberBusinessInvestmentSerializer(obj).data)

    def delete(self, request, pk):
        obj = KuberBusinessInvestment.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not obj:
            return Response({"detail": "Investment not found."}, status=404)
        obj.soft_delete()
        audit(request.user, "DELETE", "KUBER_BUSINESS_INVESTMENT", obj, "Kuber investment moved to restore bin.")
        return Response(status=204)


class InvestmentListCreateAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = Investment.objects.filter(
            deleted_at__isnull=True
        ).select_related("investor", "business", "scheme").prefetch_related("recurring_contributions")
        return page_response(request, qs, InvestmentSerializer)

    @transaction.atomic
    def post(self, request):
        serializer = InvestmentSerializer(data=request.data, context={"request": request})
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "CREATE", "INVESTMENT", obj, "Investor investment created.")
        return Response(
            InvestmentSerializer(obj, context={"request": request}).data,
            status=201,
        )


class InvestmentDetailAPIView(AdminOnlyAPIView):
    def get(self, request, pk):
        obj = Investment.objects.filter(pk=pk, deleted_at__isnull=True).select_related("investor","business","scheme").first()
        if not obj:
            return Response({"detail": "Investment not found."}, status=404)
        return Response(InvestmentSerializer(obj).data)

    def put(self, request, pk):
        obj = Investment.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not obj:
            return Response({"detail": "Investment not found."}, status=404)
        # Investment terms should normally be changed only by an explicit business rule.
        serializer = InvestmentSerializer(obj, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "UPDATE", "INVESTMENT", obj, "Investor investment updated.")
        return Response(InvestmentSerializer(obj).data)

    def delete(self, request, pk):
        obj = Investment.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not obj:
            return Response({"detail": "Investment not found."}, status=404)
        obj.soft_delete()
        audit(request.user, "DELETE", "INVESTMENT", obj, "Investor investment moved to restore bin.")
        return Response(status=204)


class BalanceSheetListCreateAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser]

    def get(self, request):
        qs = BalanceSheet.objects.filter(deleted_at__isnull=True).select_related("business", "uploaded_by")
        business_id = request.query_params.get("business")
        if business_id:
            qs = qs.filter(business_id=business_id)
        return page_response(request, qs, BalanceSheetSerializer)

    def post(self, request):
        serializer = BalanceSheetSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save(uploaded_by=request.user)
        audit(request.user, "UPLOAD", "BALANCE_SHEET", obj, "Balance sheet uploaded.")
        return Response(BalanceSheetSerializer(obj).data, status=201)


class BalanceSheetDetailAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser]

    def put(self, request, pk):
        obj = BalanceSheet.objects.filter(pk=pk, deleted_at__isnull=True).first()
        if not obj:
            return Response({"detail": "Balance sheet not found."}, status=404)
        serializer = BalanceSheetSerializer(obj, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "UPDATE", "BALANCE_SHEET", obj, "Balance sheet updated.")
        return Response(BalanceSheetSerializer(obj).data)


class LedgerListAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = LedgerEntry.objects.all().select_related("business", "investor", "investment", "kuber_investment")
        if request.query_params.get("business"):
            qs = qs.filter(business_id=request.query_params["business"])
        if request.query_params.get("investor"):
            qs = qs.filter(investor_id=request.query_params["investor"])
        if request.query_params.get("entry_type"):
            qs = qs.filter(entry_type=request.query_params["entry_type"])
        if request.query_params.get("date"):
            qs = qs.filter(entry_date=request.query_params["date"])
        return page_response(request, qs, LedgerEntrySerializer)


class GenerateDailyLedgerAPIView(AdminOnlyAPIView):
    @transaction.atomic
    def post(self, request):
        entry_date = request.data.get("date")
        if entry_date:
            from datetime import datetime
            try:
                entry_date = datetime.strptime(entry_date, "%Y-%m-%d").date()
            except ValueError:
                return Response({"detail": "date must be YYYY-MM-DD."}, status=400)
        else:
            entry_date = timezone.localdate()

        result = generate_daily_ledger(entry_date)
        return Response(result, status=200)


class PayoutListCreateAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = Payout.objects.all().select_related("investment", "investor")
        return page_response(request, qs, PayoutSerializer)

    @transaction.atomic
    def post(self, request):
        serializer = PayoutSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        payout = serializer.save(created_by=request.user)
        investment = payout.investment
        LedgerEntry.objects.create(
            business=investment.business,
            investment=investment,
            investor=investment.investor,
            entry_date=payout.payout_date,
            entry_type=LedgerEntry.EntryTypes.DEBIT,
            amount=payout.amount,
            description="Investor payout.",
            reference=f"PAYOUT-{payout.pk}",
        )
        audit(request.user, "PAYOUT", "LEDGER", payout, "Investor payout recorded.")
        return Response(PayoutSerializer(payout).data, status=201)


class CertificateListCreateAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def get(self, request):
        qs = Certificate.objects.all().select_related(
            "investment", "investment__investor", "investment__business", "investment__scheme"
        ).prefetch_related("attachments")
        return page_response(request, qs, CertificateSerializer)

    @transaction.atomic
    def post(self, request):
        investment = Investment.objects.filter(
            pk=request.data.get("investment"), deleted_at__isnull=True
        ).select_related("investor", "business", "scheme").first()
        if not investment:
            return Response({"detail": "Investment not found."}, status=404)

        rules = investment.scheme.rules or []
        rules_html = "".join(f"<li>{r}</li>" for r in rules)
        amount_label = (
            f"Monthly Installment: ₹{investment.monthly_installment}"
            if investment.scheme.category == Scheme.Categories.PLATINUM
            else f"Investment Amount: ₹{investment.amount}"
        )
        html = f"""
        <html>
          <head><title>Kuber Trobe Investment Certificate</title></head>
          <body>
            <div style="text-align:center">
              <h1>Kuber Trobe</h1>
              <h2>Investment Certificate</h2>
              <p>LOGO WATERMARK</p>
            </div>
            <hr>
            <p><b>Investor:</b> {investment.investor.name}</p>
            <p><b>Business:</b> {investment.business.name}</p>
            <p><b>{amount_label.split(':')[0]}:</b> {amount_label.split(':', 1)[1]}</p>
            <p><b>Scheme Category:</b> {investment.scheme.get_category_display()}</p>
            <p><b>Scheme:</b> {investment.scheme.name}</p>
            <p><b>Investor Rate:</b> {investment.investor_annual_rate}% p.a.</p>
            <p><b>Tenure:</b> {investment.scheme.tenure_months} months</p>
            <h3>Terms & Rules</h3>
            <ul>{rules_html}</ul>
          </body>
        </html>
        """

        certificate = Certificate.objects.filter(investment=investment).first()
        if certificate:
            certificate.html_content = html
            certificate.version += 1
            certificate.created_by = request.user
            certificate.save(update_fields=["html_content", "version", "created_by", "updated_at"])
            operation = "UPDATE"
        else:
            certificate = Certificate.objects.create(
                investment=investment, html_content=html, created_by=request.user
            )
            operation = "CREATE"

        files = request.FILES.getlist("attachments")
        titles = request.data.getlist("attachment_titles") if hasattr(request.data, "getlist") else []
        for index, file in enumerate(files):
            title = titles[index] if index < len(titles) else file.name
            CertificateAttachment.objects.create(
                certificate=certificate,
                title=title or file.name,
                file=file,
                sort_order=index,
            )

        audit(
            request.user, operation, "CERTIFICATE", certificate,
            "Investment certificate generated with attachments."
        )
        return Response(
            CertificateSerializer(certificate, context={"request": request}).data,
            status=201 if operation == "CREATE" else 200,
        )


class CertificateDetailAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def get(self, request, pk):
        obj = Certificate.objects.filter(pk=pk).prefetch_related("attachments").first()
        if not obj:
            return Response({"detail": "Certificate not found."}, status=404)
        return Response(CertificateSerializer(obj, context={"request": request}).data)

    def put(self, request, pk):
        obj = Certificate.objects.filter(pk=pk).first()
        if not obj:
            return Response({"detail": "Certificate not found."}, status=404)

        if "html_content" in request.data:
            obj.html_content = request.data["html_content"]
        obj.version += 1
        obj.created_by = request.user
        obj.save(update_fields=["html_content", "version", "created_by", "updated_at"])

        files = request.FILES.getlist("attachments")
        titles = request.data.getlist("attachment_titles") if hasattr(request.data, "getlist") else []
        start_order = obj.attachments.count()
        for index, file in enumerate(files):
            title = titles[index] if index < len(titles) else file.name
            CertificateAttachment.objects.create(
                certificate=obj, title=title or file.name, file=file, sort_order=start_order + index
            )

        audit(request.user, "UPDATE", "CERTIFICATE", obj, "Certificate updated.")
        return Response(CertificateSerializer(obj, context={"request": request}).data)

    def delete(self, request, pk):
        obj = Certificate.objects.filter(pk=pk).first()
        if not obj:
            return Response({"detail": "Certificate not found."}, status=404)
        obj.delete()
        audit(request.user, "DELETE", "CERTIFICATE", obj, "Certificate deleted.")
        return Response(status=204)


class CertificateAttachmentDeleteAPIView(AdminOnlyAPIView):
    def delete(self, request, pk, attachment_id):
        attachment = CertificateAttachment.objects.filter(
            pk=attachment_id, certificate_id=pk
        ).first()
        if not attachment:
            return Response({"detail": "Attachment not found."}, status=404)
        attachment.delete()
        return Response(status=204)


ACCOUNTANT_PERMISSION_DEFAULTS = {
    "business_add": True, "business_view": True, "business_edit": True, "business_delete": True,
    "investor_add": True, "investor_view": True, "investor_edit": True, "investor_delete": True,
    "scheme_add": True, "scheme_view": True, "scheme_edit": True, "scheme_delete": True,
    "kuber_investment_add": True, "kuber_investment_view": True, "kuber_investment_edit": True,
    "kuber_investment_delete": True,
    "investment_add": True, "investment_view": True, "investment_edit": True, "investment_delete": True,
    "balance_sheet_view": True, "balance_sheet_upload": True, "balance_sheet_edit": True,
    "ledger_view": True, "ledger_generate": True,
    "payout_view": True, "payout_create": True,
    "certificate_view": True, "certificate_generate": True,
    "audit_view": True, "restore": True,
}

class AccountantListCreateAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = User.objects.filter(role=User.Roles.ACCOUNTANT).order_by("-date_joined")
        return page_response(request, qs, UserOutputSerializer)

    def post(self, request):
        data = request.data.copy()
        data["role"] = User.Roles.ACCOUNTANT
        serializer = UserCreateSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        AccountantPermission.objects.update_or_create(
            user=user, defaults={"permissions": {**ACCOUNTANT_PERMISSION_DEFAULTS, **(request.data.get("permissions") or {})}}
        )
        audit(request.user, "CREATE", "ACCOUNTANT", user, "Accountant created.")
        data = UserOutputSerializer(user).data
        data["permissions"] = user.accountant_permissions.permissions
        return Response(data, status=201)


class AccountantDetailAPIView(AdminOnlyAPIView):
    def put(self, request, pk):
        user = User.objects.filter(pk=pk, role=User.Roles.ACCOUNTANT).first()
        if not user:
            return Response({"detail": "Accountant not found."}, status=404)
        allowed = {"email", "first_name", "last_name", "phone", "is_active"}
        for field in allowed:
            if field in request.data:
                setattr(user, field, request.data[field])
        user.save()
        if "permissions" in request.data:
            AccountantPermission.objects.update_or_create(
                user=user, defaults={"permissions": {**ACCOUNTANT_PERMISSION_DEFAULTS, **(request.data.get("permissions") or {})}}
            )
        audit(request.user, "UPDATE", "ACCOUNTANT", user, "Accountant updated.")
        data = UserOutputSerializer(user).data
        data["permissions"] = getattr(user, "accountant_permissions", None).permissions if hasattr(user, "accountant_permissions") else {}
        return Response(data)

    def delete(self, request, pk):
        user = User.objects.filter(pk=pk, role=User.Roles.ACCOUNTANT).first()
        if not user:
            return Response({"detail": "Accountant not found."}, status=404)
        user.is_active = False
        user.save(update_fields=["is_active"])
        audit(request.user, "DELETE", "ACCOUNTANT", user, "Accountant deactivated.")
        return Response(status=204)


class AccountantPermissionAPIView(AdminOnlyAPIView):
    def get(self, request, pk):
        user = User.objects.filter(pk=pk, role=User.Roles.ACCOUNTANT).first()
        if not user:
            return Response({"detail": "Accountant not found."}, status=404)
        obj, _ = AccountantPermission.objects.get_or_create(user=user)
        return Response(AccountantPermissionSerializer(obj).data)

    def put(self, request, pk):
        user = User.objects.filter(pk=pk, role=User.Roles.ACCOUNTANT).first()
        if not user:
            return Response({"detail": "Accountant not found."}, status=404)
        obj, _ = AccountantPermission.objects.get_or_create(user=user)
        serializer = AccountantPermissionSerializer(obj, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "UPDATE", "ACCOUNTANT_PERMISSION", obj, "Accountant permissions updated.")
        return Response(serializer.data)


class BusinessDocumentListAPIView(AdminOnlyAPIView):
    def get(self, request, business_id):
        qs = BusinessDocument.objects.filter(business_id=business_id).select_related("uploaded_by")
        return page_response(request, qs, BusinessDocumentSerializer)


class RecurringContributionListCreateAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def get(self, request, investment_id):
        qs = RecurringContribution.objects.filter(investment_id=investment_id)
        return page_response(request, qs, RecurringContributionSerializer)

    @transaction.atomic
    def post(self, request, investment_id):
        investment = Investment.objects.filter(
            pk=investment_id, deleted_at__isnull=True
        ).select_related("scheme").first()
        if not investment or investment.scheme.category != Scheme.Categories.PLATINUM:
            return Response({"detail": "A valid Platinum investment is required."}, status=400)
        data = request.data.copy()
        data["investment"] = investment.pk
        serializer = RecurringContributionSerializer(
            data=data, context={"investment": investment}
        )
        serializer.is_valid(raise_exception=True)
        obj = serializer.save(created_by=request.user)
        audit(request.user, "CREATE", "RECURRING_CONTRIBUTION", obj, "Platinum monthly contribution recorded.")
        return Response(RecurringContributionSerializer(obj).data, status=201)


class RecurringContributionDetailAPIView(AdminOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def put(self, request, investment_id, pk):
        obj = RecurringContribution.objects.filter(
            pk=pk, investment_id=investment_id
        ).first()
        if not obj:
            return Response({"detail": "Contribution not found."}, status=404)
        serializer = RecurringContributionSerializer(obj, data=request.data, partial=True)
        serializer.is_valid(raise_exception=True)
        obj = serializer.save()
        audit(request.user, "UPDATE", "RECURRING_CONTRIBUTION", obj, "Platinum monthly contribution updated.")
        return Response(RecurringContributionSerializer(obj).data)

    def delete(self, request, investment_id, pk):
        obj = RecurringContribution.objects.filter(
            pk=pk, investment_id=investment_id
        ).first()
        if not obj:
            return Response({"detail": "Contribution not found."}, status=404)
        obj.delete()
        audit(request.user, "DELETE", "RECURRING_CONTRIBUTION", obj, "Platinum monthly contribution deleted.")
        return Response(status=204)


class AuditLogListAPIView(AdminOnlyAPIView):
    def get(self, request):
        qs = AuditLog.objects.all().select_related("actor")
        if request.query_params.get("operation"):
            qs = qs.filter(operation=request.query_params["operation"])
        if request.query_params.get("date"):
            qs = qs.filter(created_at__date=request.query_params["date"])
        if request.query_params.get("module"):
            qs = qs.filter(module=request.query_params["module"])
        return page_response(request, qs, AuditLogSerializer)


class RestoreBinAPIView(AdminOnlyAPIView):
    model_map = {
        "business": Business,
        "investor": Investor,
        "scheme": Scheme,
        "kuber-investment": KuberBusinessInvestment,
        "investment": Investment,
        "balance-sheet": BalanceSheet,
    }

    def get(self, request):
        result = {}
        cutoff = timezone.now() - timedelta(days=30)
        for key, model in self.model_map.items():
            qs = model.objects.filter(deleted_at__isnull=False, deleted_at__gte=cutoff)
            result[key] = [
                {
                    "id": o.pk,
                    "deleted_at": o.deleted_at,
                    "label": str(o),
                } for o in qs[:100]
            ]
        return Response(result)


class RestoreItemAPIView(AdminOnlyAPIView):
    def post(self, request, resource, pk):
        model = RestoreBinAPIView.model_map.get(resource)
        if not model:
            return Response({"detail": "Unsupported restore resource."}, status=400)
        cutoff = timezone.now() - timedelta(days=30)
        obj = model.objects.filter(pk=pk, deleted_at__isnull=False, deleted_at__gte=cutoff).first()
        if not obj:
            return Response({"detail": "Item not found or restore window expired."}, status=404)
        obj.restore()
        audit(request.user, "RESTORE", resource.upper(), obj, "Item restored.")
        return Response({"detail": "Restored successfully.", "id": obj.pk})


class ExportInvestmentsAPIView(AdminOnlyAPIView):
    def get(self, request):
        from django.http import HttpResponse
        qs = Investment.objects.filter(deleted_at__isnull=True).select_related("investor","business","scheme")
        wb = Workbook()
        ws = wb.active
        ws.title = "Investments"
        ws.append(["Investor", "Business", "Amount", "Scheme Category", "Scheme", "Rate % p.a.", "Investment Date", "Maturity Date"])
        for i in qs:
            ws.append([
                i.investor.name, i.business.name, float(i.amount), i.scheme.category,
                i.scheme.name, float(i.investor_annual_rate), str(i.investment_date),
                str(i.maturity_date or ""),
            ])
        response = HttpResponse(
            content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
        )
        response["Content-Disposition"] = 'attachment; filename="kuber_trobe_investments.xlsx"'
        wb.save(response)
        return response
