from decimal import Decimal

from pathlib import Path

from django.conf import settings
from django.core.files.storage import default_storage
from django.db.models import Q, Sum
from django.http import FileResponse, Http404
from django.utils import timezone
from django.utils.text import slugify
from rest_framework import generics, mixins, permissions, status, views, viewsets
from rest_framework.decorators import action
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
from rest_framework.permissions import SAFE_METHODS
from rest_framework.response import Response

from apps.accounts.models import Role, User
from apps.accounts.services import generate_temp_password, send_investor_credentials
from apps.audit.models import AuditLog
from apps.audit.services import log_audit
from apps.cms.models import (
    BlogPost,
    CompanyProfileSection,
    ContactInquiry,
    GalleryItem,
    LeadershipProfile,
    ShowcaseVideo,
    TeamMember,
)
from apps.core.api_serializers import (
    AuditLogSerializer,
    BeneficiarySerializer,
    BlogPostDetailSerializer,
    BlogPostListSerializer,
    CompanyProfileSerializer,
    ContactInquirySerializer,
    CustomerSerializer,
    DocumentSerializer,
    GalleryItemSerializer,
    InvestmentCertificateSerializer,
    InvestmentSerializer,
    LeadershipSerializer,
    LedgerLineSerializer,
    NotificationPreferenceSerializer,
    NotificationSerializer,
    ProfitCalculationSerializer,
    ProjectInvestmentTermsSerializer,
    ProjectSaleSerializer,
    PropertyPaymentSerializer,
    PropertyUnitSerializer,
    ReportSerializer,
    ShowcaseVideoSerializer,
    StatementSerializer,
    SupportTicketSerializer,
    TeamMemberSerializer,
    TicketMessageSerializer,
    TransactionSerializer,
    TransparencyEventSerializer,
)
from apps.core.models import Visibility
from apps.core.permissions import (
    FINANCE_ROLES,
    STAFF_ROLES,
    IsAdminRole,
    IsAuthenticatedStaff,
    IsInvestorOrStaff,
)
from apps.documents.models import Document
from apps.distributions.models import ProfitCalculation, ProjectLedgerLine
from apps.distributions.services import (
    approve_profit_calculation,
    create_profit_calculation,
    execute_distribution,
    submit_profit_calculation,
)
from apps.investments.models import Investment, InvestmentCertificate
from apps.investments.services import (
    activate_investment,
    cap_table_for,
    create_investor_order,
    quote_investment,
    verify_payment,
)
from apps.investors.models import (
    ApplicationStatus,
    Beneficiary,
    Investor,
    InvestorApplication,
    InvestorStatus,
    InvestorType,
    KYCStatus,
)
from apps.investors.serializers import InvestorApplicationSerializer, InvestorSerializer
from apps.notifications.models import Notification, NotificationPreference
from apps.projects.models import Project, ProjectGalleryItem, ProjectInvestmentTerms, ProjectStatus
from apps.projects.serializers import ProjectDetailSerializer, ProjectListSerializer, ProjectWriteSerializer
from apps.sales.models import Customer, ProjectSale, PropertyUnit
from apps.sales.services import advance_sale, create_sale, record_payment, sales_dashboard
from apps.reports.models import Report
from apps.statements.models import Statement
from apps.support.models import SupportTicket, TicketMessage, TicketStatus
from apps.transactions.models import Transaction
from apps.transparency.models import TransparencyEvent



def investor_for(user):
    return getattr(user, "investor", None)


def visibility_q(user):
    if not user or not user.is_authenticated:
        return [Visibility.PUBLIC]
    if user.role in {Role.SUPER_ADMIN, Role.ADMIN}:
        return [
            Visibility.PUBLIC,
            Visibility.INVESTOR,
            Visibility.ADMIN,
            Visibility.INTERNAL,
        ]
    if user.role in {Role.PROJECT_MANAGER, Role.FINANCE_MANAGER}:
        return [Visibility.PUBLIC, Visibility.INVESTOR, Visibility.ADMIN]
    if user.role == Role.INVESTOR:
        return [Visibility.PUBLIC, Visibility.INVESTOR]
    return [Visibility.PUBLIC]


def _unique_project_slug(name, requested=""):
    base = slugify(requested or name) or "project"
    slug = base
    index = 2
    while Project.objects.filter(slug=slug).exists():
        slug = f"{base}-{index}"
        index += 1
    return slug


def _store_project_image(uploaded_file, prefix="projects"):
    extension = Path(uploaded_file.name).suffix.lower() or ".jpg"
    if extension not in {".jpg", ".jpeg", ".png", ".webp", ".gif"}:
        raise ValueError("Image must be JPG, PNG, WEBP or GIF.")
    safe_stem = slugify(Path(uploaded_file.name).stem) or "image"
    filename = f"{prefix}/{timezone.now().strftime('%Y%m%d%H%M%S')}-{safe_stem}{extension}"
    saved = default_storage.save(filename, uploaded_file)
    media_url = settings.MEDIA_URL if settings.MEDIA_URL.endswith("/") else f"{settings.MEDIA_URL}/"
    return f"{media_url}{saved}"


class ProjectViewSet(viewsets.ModelViewSet):
    lookup_field = "slug"
    filterset_fields = ["status", "project_type", "is_published"]
    search_fields = ["name", "location", "summary"]
    parser_classes = [JSONParser, FormParser, MultiPartParser]

    def get_permissions(self):
        if self.request.method in SAFE_METHODS:
            return [permissions.AllowAny()]
        return [IsAuthenticatedStaff()]

    def get_queryset(self):
        qs = Project.objects.prefetch_related(
            "financial_lines",
            "public_documents",
            "gallery_items",
            "videos",
            "milestones",
        ).select_related("investment_terms")
        user = self.request.user
        if user.is_authenticated and getattr(user, "role", None) in STAFF_ROLES:
            return qs
        return qs.filter(is_published=True)

    def get_serializer_class(self):
        if self.action in {"create", "update", "partial_update"}:
            return ProjectWriteSerializer
        if self.action == "retrieve":
            return ProjectDetailSerializer
        return ProjectListSerializer

    def _prepare_project_payload(self, request, partial=False):
        data = request.data.copy()
        for key in list(data.keys()):
            if key in {"image_file", "gallery_files"}:
                data.pop(key)

        image_file = request.FILES.get("image") or request.FILES.get("image_file")
        if image_file and getattr(image_file, "size", 0):
            data["image"] = _store_project_image(image_file)

        name = (data.get("name") or "").strip()
        if not partial and not data.get("slug") and name:
            data["slug"] = _unique_project_slug(name, data.get("slug") or "")
        elif data.get("slug"):
            data["slug"] = slugify(data.get("slug"))

        status_value = data.get("status")
        if status_value and not data.get("status_label"):
            data["status_label"] = dict(ProjectStatus.choices).get(status_value, "")

        if "is_published" in data:
            raw = data.get("is_published")
            if isinstance(raw, str):
                data["is_published"] = raw.lower() in {"1", "true", "on", "yes"}

        return data

    def create(self, request, *args, **kwargs):
        try:
            payload = self._prepare_project_payload(request)
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        if not payload.get("name") or not payload.get("location") or not payload.get("project_type"):
            return Response(
                {"success": False, "message": "Name, location and project type are required."},
                status=400,
            )
        if not payload.get("slug"):
            payload["slug"] = _unique_project_slug(payload.get("name") or "project")
        serializer = self.get_serializer(data=payload)
        serializer.is_valid(raise_exception=True)
        project = serializer.save()
        gallery_files = [
            uploaded
            for uploaded in (request.FILES.getlist("gallery") or request.FILES.getlist("gallery_files"))
            if getattr(uploaded, "size", 0)
        ]
        for order, uploaded in enumerate(gallery_files):
            try:
                image_url = _store_project_image(uploaded, prefix="projects/gallery")
            except ValueError:
                continue
            ProjectGalleryItem.objects.create(project=project, image=image_url, sort_order=order)
        log_audit(action="Project Created", module="projects", request=request, instance=project)
        return Response(ProjectListSerializer(project).data, status=status.HTTP_201_CREATED)

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop("partial", False)
        instance = self.get_object()
        try:
            payload = self._prepare_project_payload(request, partial=partial)
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        serializer = self.get_serializer(instance, data=payload, partial=partial)
        serializer.is_valid(raise_exception=True)
        project = serializer.save()
        gallery_files = [
            uploaded
            for uploaded in (request.FILES.getlist("gallery") or request.FILES.getlist("gallery_files"))
            if getattr(uploaded, "size", 0)
        ]
        if gallery_files:
            start = project.gallery_items.count()
            for order, uploaded in enumerate(gallery_files):
                try:
                    image_url = _store_project_image(uploaded, prefix="projects/gallery")
                except ValueError:
                    continue
                ProjectGalleryItem.objects.create(project=project, image=image_url, sort_order=start + order)
        log_audit(action="Project Updated", module="projects", request=request, instance=project)
        return Response(ProjectWriteSerializer(project).data)

    def perform_create(self, serializer):
        project = serializer.save()
        log_audit(action="Project Created", module="projects", request=self.request, instance=project)

    def perform_update(self, serializer):
        project = serializer.save()
        log_audit(action="Project Updated", module="projects", request=self.request, instance=project)

    @action(detail=True, methods=["get"], permission_classes=[IsAuthenticatedStaff])
    def cap_table(self, request, slug=None):
        return Response({"success": True, "data": cap_table_for(self.get_object())})

    @action(detail=True, methods=["get", "patch"], url_path="terms")
    def terms(self, request, slug=None):
        project = self.get_object()
        terms, _ = ProjectInvestmentTerms.objects.get_or_create(project=project)
        if request.method == "PATCH":
            if request.user.role not in STAFF_ROLES:
                return Response({"success": False, "message": "Staff only."}, status=403)
            serializer = ProjectInvestmentTermsSerializer(terms, data=request.data, partial=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()
            log_audit(action="Investment Terms Updated", module="projects", request=request, instance=project)
            return Response({"success": True, "data": serializer.data})
        if not terms.is_published and request.user.role not in STAFF_ROLES:
            return Response({"success": False, "message": "Terms are not published."}, status=404)
        return Response({"success": True, "data": ProjectInvestmentTermsSerializer(terms).data})


class InvestorRegisterView(generics.CreateAPIView):
    serializer_class = InvestorApplicationSerializer
    permission_classes = [permissions.AllowAny]

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        application = serializer.save()
        log_audit(action="Investor Application Submitted", module="investors", request=request, instance=application)
        return Response(
            {
                "success": True,
                "message": "Application received. Our team will review and contact you.",
                "data": serializer.data,
            },
            status=status.HTTP_201_CREATED,
        )


class InvestorMeView(views.APIView):
    permission_classes = [IsInvestorOrStaff]

    def get(self, request):
        if request.user.role == Role.INVESTOR:
            investor = investor_for(request.user)
            if investor is None:
                return Response({"success": False, "message": "Investor profile not found."}, status=404)
            return Response({"success": True, "data": InvestorSerializer(investor).data})
        qs = Investor.objects.select_related("user")
        return Response({"success": True, "data": InvestorSerializer(qs, many=True).data})


class InvestorApplicationStaffViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = InvestorApplicationSerializer
    permission_classes = [IsAuthenticatedStaff]
    queryset = InvestorApplication.objects.all()
    filterset_fields = ["status", "investor_type"]
    search_fields = ["email", "full_name", "company_name"]

    def _provision_investor(self, application, password: str):
        names = (application.full_name or application.authorized_person or application.email).split()
        user, created = User.objects.get_or_create(
            email=application.email.lower(),
            defaults={
                "first_name": names[0] if names else "",
                "last_name": " ".join(names[1:]) if len(names) > 1 else "",
                "phone": application.mobile,
                "role": Role.INVESTOR,
                "is_email_verified": True,
            },
        )
        user.set_password(password)
        if not created:
            user.phone = user.phone or application.mobile
            user.role = Role.INVESTOR
            user.is_email_verified = True
        user.save()
        code = f"SH-INV-{Investor.objects.count() + 1:04d}"
        investor, _ = Investor.objects.get_or_create(
            user=user,
            defaults={
                "account_code": code,
                "investor_type": application.investor_type or InvestorType.INDIVIDUAL,
                "status": InvestorStatus.ACTIVE,
                "kyc_status": KYCStatus.PENDING,
                "country": application.country,
                "nationality": application.nationality,
                "address": application.address,
                "company_name": application.company_name,
                "registration_number": application.registration_number,
                "application": application,
            },
        )
        return investor

    @action(detail=True, methods=["post"], permission_classes=[IsAdminRole])
    def approve(self, request, pk=None):
        application = self.get_object()
        if application.status == ApplicationStatus.APPROVED:
            return Response({"success": False, "message": "Application is already approved."}, status=400)
        password = generate_temp_password()
        investor = self._provision_investor(application, password)
        application.status = ApplicationStatus.APPROVED
        application.reviewed_by = request.user
        application.reviewed_at = timezone.now()
        application.save()
        log_audit(action="Investor Application Approved", module="investors", request=request, instance=application)
        email_sent = True
        try:
            send_investor_credentials(
                investor.user.email,
                password,
                application.full_name or application.authorized_person or "",
            )
        except Exception:
            email_sent = False
        payload = {
            "account_code": investor.account_code,
            "email": investor.user.email,
            "email_sent": email_sent,
        }
        if not email_sent:
            payload["temporary_password"] = password
        return Response(
            {
                "success": True,
                "message": (
                    f"Approved. Login email sent to {investor.user.email}."
                    if email_sent
                    else f"Approved, but the login email failed. Temporary password: {password}"
                ),
                "data": payload,
            }
        )

    @action(detail=True, methods=["post"], permission_classes=[IsAdminRole])
    def reject(self, request, pk=None):
        application = self.get_object()
        application.status = ApplicationStatus.REJECTED
        application.review_notes = request.data.get("notes", application.review_notes)
        application.reviewed_by = request.user
        application.reviewed_at = timezone.now()
        application.save()
        log_audit(action="Investor Application Rejected", module="investors", request=request, instance=application)
        return Response({"success": True, "message": "Application rejected."})


class InvestorStaffViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = InvestorSerializer
    permission_classes = [IsAuthenticatedStaff]
    queryset = Investor.objects.select_related("user").all()
    filterset_fields = ["status", "investor_type", "kyc_status"]
    search_fields = ["account_code", "user__email", "company_name"]


class DashboardView(views.APIView):
    permission_classes = [IsInvestorOrStaff]

    def get(self, request):
        investor = investor_for(request.user)
        if request.user.role != Role.INVESTOR or investor is None:
            return Response({"success": False, "message": "Investor dashboard is available to investor accounts."}, status=403)

        investments = list(investor.investments.select_related("project").all())
        primary = next((row for row in investments if row.status == "active"), investments[0] if investments else None)
        totals = investor.investments.aggregate(
            committed=Sum("committed_amount"),
            received=Sum("received_amount"),
            expected=Sum("expected_benefit"),
            benefit=Sum("benefit_received"),
            participation=Sum("participation_percent"),
        )
        from apps.distributions.models import DistributionItem

        pending_dist = investor.distribution_items.exclude(status="paid").aggregate(total=Sum("amount"))["total"] or Decimal("0")
        active_projects = {row.project_id for row in investments if row.status == "active"}
        metrics = {
            "account_code": investor.account_code,
            "total_investment": str(totals["committed"] or Decimal("0")),
            "total_received": str(totals["received"] or Decimal("0")),
            "expected_benefit": str(totals["expected"] or Decimal("0")),
            "benefit_received": str(totals["benefit"] or Decimal("0")),
            "current_project": primary.project.name if primary else None,
            "current_project_slug": primary.project.slug if primary else None,
            "investment_status": primary.status if primary else None,
            "project_progress": str(primary.project.progress_percent) if primary else "0",
            "next_distribution_on": primary.next_distribution_on if primary else None,
            "agreement_code": primary.agreement_code if primary else None,
            "active_projects": len(active_projects),
            "total_participation": str(totals["participation"] or Decimal("0")),
            "realized_distributions": str(totals["benefit"] or Decimal("0")),
            "pending_distributions": str(pending_dist),
            "kyc_status": investor.kyc_status,
        }
        progress_series = [
            {"label": event.stamp_label or event.occurred_at.strftime("%b"), "value": float(event.progress_percent)}
            for event in TransparencyEvent.objects.filter(
                project=primary.project if primary else None,
                event_type="progress",
                visibility__in=visibility_q(request.user),
                progress_percent__isnull=False,
            ).order_by("occurred_at")[:12]
        ]
        return Response(
            {
                "success": True,
                "data": {
                    "investor": InvestorSerializer(investor).data,
                    "metrics": metrics,
                    "investments": InvestmentSerializer(investments, many=True).data,
                    "progress_series": progress_series,
                },
            }
        )


class InvestmentViewSet(viewsets.ModelViewSet):
    serializer_class = InvestmentSerializer
    permission_classes = [IsInvestorOrStaff]
    filterset_fields = ["status", "project"]
    http_method_names = ["get", "post", "patch", "head", "options"]

    def get_queryset(self):
        qs = Investment.objects.select_related("project", "investor", "investor__user").prefetch_related("allocations")
        if self.request.user.role == Role.INVESTOR:
            investor = investor_for(self.request.user)
            return qs.filter(investor=investor) if investor else qs.none()
        if self.request.user.role not in FINANCE_ROLES | {Role.PROJECT_MANAGER, Role.SUPPORT_STAFF}:
            return qs.none()
        return qs

    def get_permissions(self):
        # quote/invest are investor marketplace POSTs; other writes stay staff-only
        if getattr(self, "action", None) in ("quote", "invest"):
            return [IsInvestorOrStaff()]
        if self.request.method in SAFE_METHODS:
            return [IsInvestorOrStaff()]
        return [IsAuthenticatedStaff()]

    def perform_create(self, serializer):
        from apps.investments.services import next_agreement_code, next_investment_reference

        extra = {}
        if not serializer.validated_data.get("agreement_code"):
            extra["agreement_code"] = next_agreement_code(serializer.validated_data["project"])
        extra.setdefault("reference", next_investment_reference())
        investment = serializer.save(**extra)
        log_audit(action="Investment Created", module="investments", request=self.request, instance=investment)

    @action(detail=False, methods=["post"], permission_classes=[IsInvestorOrStaff])
    def quote(self, request):
        project = Project.objects.filter(slug=request.data.get("project") or request.data.get("project_slug")).first()
        if project is None:
            return Response({"success": False, "message": "Project not found."}, status=404)
        try:
            data = quote_investment(project, request.data.get("amount") or 0)
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        return Response({"success": True, "data": data})

    @action(detail=False, methods=["post"], permission_classes=[IsInvestorOrStaff])
    def invest(self, request):
        investor = investor_for(request.user)
        if investor is None:
            return Response({"success": False, "message": "Investor profile required."}, status=403)
        project = Project.objects.filter(slug=request.data.get("project") or request.data.get("project_slug")).first()
        if project is None:
            return Response({"success": False, "message": "Project not found."}, status=404)
        try:
            investment = create_investor_order(
                investor,
                project,
                request.data.get("amount") or 0,
                payment_method=request.data.get("payment_method") or "bank_transfer",
                payment_reference=request.data.get("payment_reference") or "",
                accept_agreement=bool(request.data.get("accept_agreement")),
            )
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        log_audit(action="Investment Order Created", module="investments", request=request, instance=investment)
        return Response({"success": True, "data": InvestmentSerializer(investment).data}, status=201)

    @action(detail=True, methods=["post"], permission_classes=[IsAuthenticatedStaff], url_path="verify-payment")
    def verify_payment(self, request, pk=None):
        investment = self.get_object()
        verify_payment(
            investment,
            request.user,
            payment_reference=request.data.get("payment_reference") or "",
            received_amount=request.data.get("received_amount"),
        )
        log_audit(action="Investment Payment Verified", module="investments", request=request, instance=investment)
        return Response({"success": True, "data": InvestmentSerializer(investment).data})

    @action(detail=True, methods=["post"], permission_classes=[IsAuthenticatedStaff])
    def activate(self, request, pk=None):
        try:
            investment = activate_investment(self.get_object(), request.user)
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        log_audit(action="Investment Activated", module="investments", request=request, instance=investment)
        return Response({"success": True, "data": InvestmentSerializer(investment).data})

    @action(detail=True, methods=["get"])
    def certificate(self, request, pk=None):
        from django.http import HttpResponse

        investment = self.get_object()
        cert = getattr(investment, "certificate", None)
        if cert is None:
            return Response({"success": False, "message": "Certificate not issued yet."}, status=404)
        if request.query_params.get("download"):
            body = (
                f"SHOPNO HOLDINGS LIMITED\nInvestment Certificate\n\n"
                f"Certificate: {cert.certificate_number}\n"
                f"Reference: {investment.reference or investment.agreement_code}\n"
                f"Investor: {investment.investor.account_code}\n"
                f"Project: {investment.project.name}\n"
                f"Amount: {investment.committed_amount} {investment.currency}\n"
                f"Participation: {investment.participation_percent}%\n"
                f"Status: {investment.status}\n\n"
                "This certificate records contractual participation. It is not a guaranteed return.\n"
            )
            response = HttpResponse(body, content_type="text/plain; charset=utf-8")
            response["Content-Disposition"] = f'attachment; filename="{cert.certificate_number}.txt"'
            return response
        return Response({"success": True, "data": InvestmentCertificateSerializer(cert).data})


class TransactionViewSet(viewsets.ModelViewSet):
    serializer_class = TransactionSerializer
    permission_classes = [IsInvestorOrStaff]
    filterset_fields = ["txn_type", "status", "project", "investment"]
    http_method_names = ["get", "post", "patch", "head", "options"]

    def get_queryset(self):
        qs = Transaction.objects.select_related("project", "investor", "investor__user")
        if self.request.user.role == Role.INVESTOR:
            investor = investor_for(self.request.user)
            return qs.filter(investor=investor) if investor else qs.none()
        return qs

    def get_permissions(self):
        if self.request.method in SAFE_METHODS:
            return [IsInvestorOrStaff()]
        return [IsAuthenticatedStaff()]

    def perform_create(self, serializer):
        txn = serializer.save(posted_by=self.request.user)
        log_audit(action="Payment Recorded", module="transactions", request=self.request, instance=txn)


class DocumentViewSet(viewsets.ModelViewSet):
    serializer_class = DocumentSerializer
    permission_classes = [IsInvestorOrStaff]
    filterset_fields = ["category", "visibility", "investment", "project"]
    parser_classes = [JSONParser, FormParser, MultiPartParser]
    http_method_names = ["get", "post", "patch", "head", "options"]

    def get_queryset(self):
        qs = Document.objects.filter(visibility__in=visibility_q(self.request.user))
        if self.request.user.role == Role.INVESTOR:
            investor = investor_for(self.request.user)
            if investor is None:
                return qs.none()
            project_ids = investor.investments.values_list("project_id", flat=True)
            return qs.filter(Q(investor=investor) | Q(investor__isnull=True, project_id__in=project_ids)).distinct()
        return qs

    def get_permissions(self):
        if self.request.method in SAFE_METHODS:
            return [IsInvestorOrStaff()]
        return [IsAuthenticatedStaff()]

    def perform_create(self, serializer):
        document = serializer.save(uploaded_by=self.request.user)
        log_audit(action="Document Uploaded", module="documents", request=self.request, instance=document)

    @action(detail=True, methods=["get"])
    def download(self, request, pk=None):
        document = self.get_object()
        if not document.file:
            raise Http404("File not stored.")
        log_audit(action="Document Accessed", module="documents", request=request, instance=document)
        return FileResponse(document.file.open("rb"), as_attachment=True, filename=document.original_name or document.title)


class StatementViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = StatementSerializer
    permission_classes = [IsInvestorOrStaff]
    filterset_fields = ["period"]

    def get_queryset(self):
        qs = Statement.objects.select_related("investor", "investment")
        if self.request.user.role == Role.INVESTOR:
            investor = investor_for(self.request.user)
            return qs.filter(investor=investor) if investor else qs.none()
        return qs

    @action(detail=True, methods=["get"])
    def download(self, request, pk=None):
        statement = self.get_object()
        if statement.document_id and statement.document and statement.document.file:
            log_audit(action="Document Accessed", module="statements", request=request, instance=statement)
            return FileResponse(
                statement.document.file.open("rb"),
                as_attachment=True,
                filename=statement.document.original_name or f"{statement.title}.pdf",
            )
        body = (
            f"{statement.title}\n"
            f"Period: {statement.period_label}\n"
            f"Posted: {statement.posted_on}\n\n"
            f"{statement.summary or 'Accounting-backed investor statement.'}\n"
        )
        from django.http import HttpResponse

        response = HttpResponse(body, content_type="text/plain; charset=utf-8")
        response["Content-Disposition"] = f'attachment; filename="{statement.title}.txt"'
        return response


class NotificationViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    serializer_class = NotificationSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        return Notification.objects.filter(user=self.request.user)

    @action(detail=True, methods=["post"])
    def read(self, request, pk=None):
        item = self.get_object()
        if item.read_at is None:
            item.read_at = timezone.now()
            item.status = "read"
            item.save(update_fields=["read_at", "status"])
        return Response({"success": True, "data": NotificationSerializer(item).data})

    @action(detail=False, methods=["get", "patch"], url_path="preferences")
    def preferences(self, request):
        prefs, _ = NotificationPreference.objects.get_or_create(user=request.user)
        if request.method == "PATCH":
            serializer = NotificationPreferenceSerializer(prefs, data=request.data, partial=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()
            log_audit(action="Security Setting Changed", module="notifications", request=request)
            return Response({"success": True, "data": serializer.data})
        return Response({"success": True, "data": NotificationPreferenceSerializer(prefs).data})


class TransparencyViewSet(viewsets.ModelViewSet):
    serializer_class = TransparencyEventSerializer
    filterset_fields = ["event_type", "period", "project", "visibility"]
    http_method_names = ["get", "post", "patch", "head", "options"]

    def get_permissions(self):
        if self.request.method in SAFE_METHODS:
            return [permissions.AllowAny()]
        return [IsAuthenticatedStaff()]

    def get_queryset(self):
        qs = TransparencyEvent.objects.select_related("project")
        user = self.request.user
        if not (user.is_authenticated and getattr(user, "role", None) in STAFF_ROLES):
            qs = qs.filter(visibility__in=visibility_q(user))
        slug = self.request.query_params.get("project_slug")
        if slug:
            qs = qs.filter(project__slug=slug)
        return qs

    def perform_create(self, serializer):
        event = serializer.save(created_by=self.request.user)
        log_audit(action="Project Progress Updated", module="transparency", request=self.request, instance=event)


class ReportViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ReportSerializer
    permission_classes = [permissions.AllowAny]
    filterset_fields = ["report_type", "visibility"]

    def get_queryset(self):
        return Report.objects.select_related("project").filter(visibility__in=visibility_q(self.request.user))


class SupportTicketViewSet(viewsets.ModelViewSet):
    serializer_class = SupportTicketSerializer
    permission_classes = [IsInvestorOrStaff]
    http_method_names = ["get", "post", "patch", "head", "options"]

    def get_queryset(self):
        qs = SupportTicket.objects.prefetch_related("messages").select_related("created_by")
        if self.request.user.role == Role.INVESTOR:
            return qs.filter(created_by=self.request.user)
        return qs

    def perform_create(self, serializer):
        investor = investor_for(self.request.user)
        count = SupportTicket.objects.count() + 1
        ticket = serializer.save(
            created_by=self.request.user,
            investor=investor,
            reference=f"SUP-{timezone.now():%Y%m%d}-{count:04d}",
            status=TicketStatus.OPEN,
        )
        body = self.request.data.get("body") or self.request.data.get("message")
        if body:
            TicketMessage.objects.create(ticket=ticket, author=self.request.user, body=body)
        log_audit(action="Support Ticket Created", module="support", request=self.request, instance=ticket)

    @action(detail=True, methods=["post"])
    def messages(self, request, pk=None):
        ticket = self.get_object()
        serializer = TicketMessageSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        msg = TicketMessage.objects.create(
            ticket=ticket,
            author=request.user,
            body=serializer.validated_data["body"],
            is_internal=bool(request.data.get("is_internal")) and request.user.role != Role.INVESTOR,
        )
        return Response({"success": True, "data": TicketMessageSerializer(msg).data}, status=201)


class AuditLogViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = AuditLogSerializer
    permission_classes = [IsAdminRole]
    queryset = AuditLog.objects.select_related("user")
    filterset_fields = ["module", "action"]
    search_fields = ["action", "object_repr", "user__email"]


class BeneficiaryViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = BeneficiarySerializer
    permission_classes = [permissions.AllowAny]

    def get_queryset(self):
        qs = Beneficiary.objects.select_related("project", "investor")
        if self.request.user.is_authenticated and self.request.user.role == Role.INVESTOR:
            investor = investor_for(self.request.user)
            return qs.filter(investor=investor) | qs.filter(is_public=True)
        if self.request.user.is_authenticated and self.request.user.role in {
            Role.SUPER_ADMIN,
            Role.ADMIN,
            Role.FINANCE_MANAGER,
        }:
            return qs
        return qs.filter(is_public=True)


class BlogViewSet(viewsets.ModelViewSet):
    lookup_field = "slug"
    filterset_fields = ["category", "featured", "is_published"]
    search_fields = ["title", "excerpt"]
    http_method_names = ["get", "post", "patch", "put", "head", "options"]

    def get_permissions(self):
        if self.request.method in SAFE_METHODS:
            return [permissions.AllowAny()]
        return [IsAuthenticatedStaff()]

    def get_queryset(self):
        qs = BlogPost.objects.all()
        user = self.request.user
        if user.is_authenticated and getattr(user, "role", None) in STAFF_ROLES:
            return qs
        return qs.filter(is_published=True)

    def get_serializer_class(self):
        if self.action == "retrieve" or self.action in {"create", "update", "partial_update"}:
            return BlogPostDetailSerializer
        return BlogPostListSerializer


class GalleryViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = GalleryItemSerializer
    permission_classes = [permissions.AllowAny]
    queryset = GalleryItem.objects.filter(is_published=True)
    filterset_fields = ["subject"]


class VideoViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = ShowcaseVideoSerializer
    permission_classes = [permissions.AllowAny]
    queryset = ShowcaseVideo.objects.filter(is_published=True)
    filterset_fields = ["category", "featured"]


class LeadershipViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = LeadershipSerializer
    permission_classes = [permissions.AllowAny]
    queryset = LeadershipProfile.objects.filter(is_published=True)


class TeamViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = TeamMemberSerializer
    permission_classes = [permissions.AllowAny]
    queryset = TeamMember.objects.filter(is_published=True)


class CompanyProfileViewSet(viewsets.ReadOnlyModelViewSet):
    serializer_class = CompanyProfileSerializer
    permission_classes = [permissions.AllowAny]
    queryset = CompanyProfileSection.objects.filter(is_published=True)


class ContactInquiryViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, viewsets.GenericViewSet):
    serializer_class = ContactInquirySerializer
    queryset = ContactInquiry.objects.all()
    filterset_fields = ["status", "interest"]
    search_fields = ["name", "email", "message"]
    http_method_names = ["get", "post", "patch", "head", "options"]

    def get_permissions(self):
        if self.action == "create":
            return [permissions.AllowAny()]
        return [IsAuthenticatedStaff()]

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        inquiry = serializer.save()
        log_audit(action="Contact Inquiry Submitted", module="cms", request=request, instance=inquiry)
        return Response(
            {"success": True, "message": "Thank you. Your enquiry has been received.", "data": serializer.data},
            status=201,
        )


class StaffOverviewView(views.APIView):
    permission_classes = [IsAuthenticatedStaff]

    def get(self, request):
        recent = AuditLog.objects.select_related("user")[:8]
        return Response(
            {
                "success": True,
                "data": {
                    "investors": Investor.objects.count(),
                    "pending_applications": InvestorApplication.objects.filter(status=ApplicationStatus.PENDING).count(),
                    "projects": Project.objects.count(),
                    "investments": Investment.objects.count(),
                    "transactions": Transaction.objects.count(),
                    "open_tickets": SupportTicket.objects.exclude(status__in=[TicketStatus.RESOLVED, TicketStatus.CLOSED]).count(),
                    "new_inquiries": ContactInquiry.objects.filter(status=ContactInquiry.Status.NEW).count(),
                    "recent_audit": AuditLogSerializer(recent, many=True).data,
                    "pending_investments": Investment.objects.exclude(status__in=["active", "completed", "cancelled"]).count(),
                    "open_sales": ProjectSale.objects.exclude(stage="recognized").count(),
                    "pending_distributions": ProfitCalculation.objects.filter(status="in_review").count(),
                },
            }
        )


class PropertyUnitViewSet(viewsets.ModelViewSet):
    serializer_class = PropertyUnitSerializer
    permission_classes = [IsAuthenticatedStaff]
    filterset_fields = ["project", "status"]
    queryset = PropertyUnit.objects.select_related("project")


class CustomerViewSet(viewsets.ModelViewSet):
    serializer_class = CustomerSerializer
    permission_classes = [IsAuthenticatedStaff]
    search_fields = ["name", "email", "phone"]
    queryset = Customer.objects.all()


class ProjectSaleViewSet(viewsets.ModelViewSet):
    serializer_class = ProjectSaleSerializer
    permission_classes = [IsAuthenticatedStaff]
    filterset_fields = ["project", "stage"]
    queryset = ProjectSale.objects.select_related("project", "unit", "customer").prefetch_related("payments")
    http_method_names = ["get", "post", "patch", "head", "options"]

    def create(self, request, *args, **kwargs):
        project = Project.objects.filter(pk=request.data.get("project")).first()
        unit = PropertyUnit.objects.filter(pk=request.data.get("unit")).first()
        customer = Customer.objects.filter(pk=request.data.get("customer")).first()
        if not all([project, unit, customer]):
            return Response({"success": False, "message": "Project, unit and customer are required."}, status=400)
        if unit.project_id != project.id:
            return Response({"success": False, "message": "Selected unit does not belong to that project."}, status=400)
        try:
            sale = create_sale(
                project,
                unit,
                customer,
                request.data.get("contracted_amount"),
                request.user,
                notes=request.data.get("notes") or "",
            )
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        log_audit(action="Property Sale Created", module="sales", request=request, instance=sale)
        return Response({"success": True, "data": ProjectSaleSerializer(sale).data}, status=201)

    @action(detail=True, methods=["post"])
    def advance(self, request, pk=None):
        sale = advance_sale(self.get_object(), request.user, request.data.get("stage"))
        return Response({"success": True, "data": ProjectSaleSerializer(sale).data})

    @action(detail=True, methods=["post"])
    def payments(self, request, pk=None):
        payment = record_payment(
            self.get_object(),
            request.data.get("amount") or 0,
            request.user,
            reference=request.data.get("reference") or "",
        )
        return Response({"success": True, "data": PropertyPaymentSerializer(payment).data}, status=201)

    @action(detail=False, methods=["get"])
    def dashboard(self, request):
        slug = request.query_params.get("project_slug")
        project = Project.objects.filter(slug=slug).first() if slug else Project.objects.first()
        if project is None:
            return Response({"success": False, "message": "Project not found."}, status=404)
        return Response({"success": True, "data": sales_dashboard(project)})


class LedgerLineViewSet(viewsets.ModelViewSet):
    serializer_class = LedgerLineSerializer
    permission_classes = [IsAuthenticatedStaff]
    filterset_fields = ["project", "kind", "recognized"]
    queryset = ProjectLedgerLine.objects.select_related("project")


class ProfitCalculationViewSet(viewsets.ModelViewSet):
    serializer_class = ProfitCalculationSerializer
    permission_classes = [IsAuthenticatedStaff]
    filterset_fields = ["project", "status"]
    queryset = ProfitCalculation.objects.select_related("project", "workflow", "distribution").prefetch_related(
        "workflow__records", "distribution__items"
    )
    http_method_names = ["get", "post", "patch", "head", "options"]

    def create(self, request, *args, **kwargs):
        project = Project.objects.filter(pk=request.data.get("project")).first()
        if project is None:
            return Response({"success": False, "message": "Project is required."}, status=400)
        calc = create_profit_calculation(
            project,
            request.user,
            request.data.get("sales_revenue") or 0,
            request.data.get("eligible_costs") or 0,
            notes=request.data.get("notes") or "",
        )
        log_audit(action="Profit Calculation Created", module="distributions", request=request, instance=calc)
        return Response({"success": True, "data": ProfitCalculationSerializer(calc).data}, status=201)

    @action(detail=True, methods=["post"])
    def submit(self, request, pk=None):
        try:
            calc = submit_profit_calculation(self.get_object(), request.user)
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        return Response({"success": True, "data": ProfitCalculationSerializer(calc).data})

    @action(detail=True, methods=["post"])
    def approve(self, request, pk=None):
        decision = request.data.get("decision") or "approved"
        try:
            calc = approve_profit_calculation(
                self.get_object(),
                request.user,
                decision,
                notes=request.data.get("notes") or "",
            )
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        return Response({"success": True, "data": ProfitCalculationSerializer(calc).data})

    @action(detail=True, methods=["post"])
    def execute(self, request, pk=None):
        try:
            execute_distribution(self.get_object(), request.user)
        except ValueError as exc:
            return Response({"success": False, "message": str(exc)}, status=400)
        calc = self.get_object()
        return Response({"success": True, "data": ProfitCalculationSerializer(calc).data})
