import json

from django.contrib.auth.password_validation import validate_password
from django.db import transaction
from rest_framework import status
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser
from rest_framework.response import Response
from rest_framework.views import APIView

from admin_panel.models import (
    User,
    Business,
    AccountantDetailField,
    AccountantDocument,
    BalanceSheet,
)
from admin_panel.serializers import BalanceSheetSerializer
from admin_panel.services import audit

from .models import BusinessAccountant
from .serializers import BusinessAccountantSerializer


class BusinessOnlyAPIView(APIView):
    permission_classes = []

    def initial(self, request, *args, **kwargs):
        super().initial(request, *args, **kwargs)
        user = request.user
        if (
            not user
            or not user.is_authenticated
            or not user.is_active
            or user.role != User.Roles.BUSINESS
        ):
            from rest_framework.exceptions import PermissionDenied
            raise PermissionDenied("You do not have permission to perform this action.")


class BusinessAccountantOnlyAPIView(APIView):
    permission_classes = []

    def initial(self, request, *args, **kwargs):
        super().initial(request, *args, **kwargs)
        user = request.user
        if (
            not user
            or not user.is_authenticated
            or not user.is_active
            or user.role != User.Roles.BUSINESS_ACCOUNTANT
        ):
            from rest_framework.exceptions import PermissionDenied
            raise PermissionDenied("You do not have permission to perform this action.")
        if not BusinessAccountant.objects.filter(user=user).exists():
            from rest_framework.exceptions import PermissionDenied
            raise PermissionDenied("Business Accountant account is not linked to a business.")


def _business_for_user(user):
    return Business.objects.filter(
        user=user,
        deleted_at__isnull=True,
    ).first()


class BusinessAccountantMeAPIView(BusinessAccountantOnlyAPIView):
    def get(self, request):
        profile = BusinessAccountant.objects.select_related("business", "user").filter(user=request.user).first()
        if not profile:
            return Response({"detail": "Business Accountant account is not linked to a business."}, status=404)
        data = BusinessAccountantSerializer(profile, context={"request": request}).data
        data["business_id"] = profile.business_id
        data["business_name"] = profile.business.name
        return Response(data)


class BusinessAccountantListCreateAPIView(BusinessOnlyAPIView):
    parser_classes = [MultiPartParser, FormParser, JSONParser]

    def get(self, request):
        business = _business_for_user(request.user)
        if not business:
            return Response({"detail": "Business account is not linked to a Business record."}, status=404)

        qs = BusinessAccountant.objects.filter(
            business=business,
        ).select_related("user").prefetch_related(
            "user__accountant_detail_fields",
            "user__accountant_documents",
        )
        return Response({
            "results": BusinessAccountantSerializer(
                qs, many=True, context={"request": request}
            ).data,
            "count": qs.count(),
            "next": None,
            "previous": None,
        })

    @transaction.atomic
    def post(self, request):
        business = _business_for_user(request.user)
        if not business:
            return Response({"detail": "Business account is not linked to a Business record."}, status=404)

        username = str(request.data.get("username", "")).strip()
        password = request.data.get("password") or ""
        email = str(request.data.get("email", "")).strip()
        if not username:
            return Response({"username": "Username is required."}, status=400)
        if not password:
            return Response({"password": "Password is required."}, status=400)
        if not email:
            return Response({"email": "Email is required."}, status=400)
        if User.objects.filter(username=username).exists():
            return Response({"username": "A user with this username already exists."}, status=400)
        if User.objects.filter(email=email).exists():
            return Response({"email": "A user with this email already exists."}, status=400)

        try:
            validate_password(password)
        except Exception as exc:
            messages = []
            for error in getattr(exc, "error_list", []):
                messages.extend(error.messages)
            return Response({"password": messages or [str(exc)]}, status=400)

        identity_details = request.data.get("identity_details", "[]")
        if isinstance(identity_details, str):
            try:
                identity_details = json.loads(identity_details)
            except json.JSONDecodeError:
                return Response({"detail": "identity_details must be valid JSON."}, status=400)
        if not isinstance(identity_details, list):
            return Response({"detail": "identity_details must be a list."}, status=400)

        user = User.objects.create_user(
            username=username,
            email=email,
            password=password,
            first_name=request.data.get("first_name", ""),
            last_name=request.data.get("last_name", ""),
            phone=request.data.get("phone", ""),
            role=User.Roles.BUSINESS_ACCOUNTANT,
            is_active=True,
        )
        user.admin_visible_password = password
        if request.FILES.get("image"):
            user.image = request.FILES["image"]
        user.save()

        profile = BusinessAccountant.objects.create(
            business=business,
            user=user,
            created_by=request.user,
        )

        for item in identity_details:
            if not isinstance(item, dict) or not str(item.get("key", "")).strip():
                return Response({"detail": "Each identity detail must contain a key."}, status=400)
            AccountantDetailField.objects.create(
                accountant=user,
                key=str(item["key"]).strip(),
                value=str(item.get("value", "")),
            )

        files = request.FILES.getlist("identity_files")
        titles = request.data.getlist("identity_files_titles") if hasattr(request.data, "getlist") else []
        for index, file in enumerate(files):
            title = titles[index] if index < len(titles) else file.name
            AccountantDocument.objects.create(
                accountant=user,
                title=title or file.name,
                file=file,
                uploaded_by=request.user,
            )

        audit(request.user, "CREATE", "BUSINESS_ACCOUNTANT", profile, "Business Accountant created.")
        return Response(
            BusinessAccountantSerializer(profile, context={"request": request}).data,
            status=status.HTTP_201_CREATED,
        )


class BusinessAccountantDetailAPIView(BusinessOnlyAPIView):
    def delete(self, request, pk):
        business = _business_for_user(request.user)
        if not business:
            return Response({"detail": "Business account is not linked to a Business record."}, status=404)
        profile = BusinessAccountant.objects.filter(pk=pk, business=business).select_related("user").first()
        if not profile:
            return Response({"detail": "Business Accountant not found."}, status=404)
        profile.user.is_active = False
        profile.user.save(update_fields=["is_active"])
        audit(request.user, "DELETE", "BUSINESS_ACCOUNTANT", profile, "Business Accountant deactivated.")
        return Response(status=204)


class BusinessAccountantBalanceSheetAPIView(BusinessAccountantOnlyAPIView):
    """The only business-accountant module: upload/view Balance Sheets for its business."""

    def get(self, request):
        profile = BusinessAccountant.objects.select_related("business").filter(user=request.user).first()
        if not profile:
            return Response({"detail": "Business Accountant account is not linked to a business."}, status=404)
        qs = BalanceSheet.objects.filter(
            business=profile.business,
            deleted_at__isnull=True,
        ).select_related("business", "entered_by").order_by("-statement_date", "-id")
        return Response({
            "results": BalanceSheetSerializer(qs, many=True, context={"request": request}).data,
            "count": qs.count(),
            "next": None,
            "previous": None,
        })

    @transaction.atomic
    def post(self, request):
        profile = BusinessAccountant.objects.select_related("business").filter(user=request.user).first()
        if not profile:
            return Response({"detail": "Business Accountant account is not linked to a business."}, status=404)

        allowed = {
            "statement_date", "cash_in_hand", "bank_balance", "accounts_receivable",
            "inventory", "prepaid_expenses", "other_current_assets", "fixed_assets",
            "accumulated_depreciation", "other_non_current_assets", "accounts_payable",
            "short_term_borrowings", "accrued_expenses", "taxes_payable",
            "other_current_liabilities", "long_term_loans", "other_non_current_liabilities",
            "owner_capital", "reserves_surplus", "retained_earnings", "drawings", "remarks",
        }
        payload = {key: request.data.get(key) for key in allowed if key in request.data}
        payload["business"] = profile.business.pk
        serializer = BalanceSheetSerializer(data=payload, context={"request": request})
        serializer.is_valid(raise_exception=True)
        statement_date = serializer.validated_data["statement_date"]
        if BalanceSheet.objects.filter(
            business=profile.business,
            statement_date=statement_date,
            deleted_at__isnull=True,
        ).exists():
            return Response({"detail": "A balance sheet already exists for this business and date."}, status=400)

        balance = serializer.save(entered_by=request.user)
        audit(request.user, "CREATE", "BALANCE_SHEET", balance, "Balance sheet uploaded by Business Accountant.")
        return Response(
            BalanceSheetSerializer(balance, context={"request": request}).data,
            status=status.HTTP_201_CREATED,
        )
