# import json
# from decimal import Decimal

# from django.contrib.auth.password_validation import validate_password
# from django.db.models import Sum
# from rest_framework import serializers

# from .models import (
#     User,
#     Business,
#     BusinessBankDetail,
#     BusinessDetailField,
#     BusinessDocument,
#     Investor,
#     InvestorDetailField,
#     InvestorDocument,
#     AccountantDetailField,
#     AccountantDocument,
#     Scheme,
#     KuberBusinessInvestment,
#     Investment,
#     BalanceSheet,
#     DailyFinancialStatement,
#     LedgerEntry,
#     Payout,
#     Certificate,
#     CertificateAttachment,
#     AuditLog,
#     RecurringContribution,
#     AccountantPermission,
# )

# from .services import (
#     maturity_value,
#     premature_value,
#     interest_till_date,
# )


# # ============================================================
# # USER SERIALIZERS
# # ============================================================

# class UserCreateSerializer(serializers.ModelSerializer):
#     password = serializers.CharField(
#         write_only=True,
#         min_length=8
#     )

#     class Meta:
#         model = User
#         fields = (
#             "id",
#             "username",
#             "email",
#             "password",
#             "first_name",
#             "last_name",
#             "phone",
#             "image",
#             "role",
#         )

#     def validate_password(self, value):
#         validate_password(value)
#         return value

#     def create(self, validated_data):
#         password = validated_data.pop("password")

#         user = User(**validated_data)
#         user.set_password(password)
#         user.save()

#         return user


# class UserOutputSerializer(serializers.ModelSerializer):

#     image = serializers.SerializerMethodField()

#     class Meta:
#         model = User
#         fields = (
#             "id",
#             "username",
#             "email",
#             "first_name",
#             "last_name",
#             "phone",
#             "role",
#             "image",
#             "is_active",
#             "is_staff",
#             "is_superuser",
#         )


#     def get_image(self, obj):
#         request = self.context.get("request")
#         image = None
#         if obj.role == User.Roles.BUSINESS:
#             business = getattr(obj, "business_profile", None)
#             if business and business.business_photo:
#                 image = business.business_photo
#         elif obj.role == User.Roles.INVESTOR:
#             investor = getattr(obj, "investor_profile", None)
#             if investor and investor.image:
#                 image = investor.image
#         elif obj.role == User.Roles.ACCOUNTANT:
#             image = obj.image
#         else:
#             image = obj.image
#         if not image:
#             return None
#         try:
#             return request.build_absolute_uri(image.url) if request else image.url
#         except Exception:
#             return None


# # ============================================================
# # KEY VALUE RELATION FIELD
# # ============================================================

# class KeyValueRelationField(serializers.Field):

#     def __init__(self, relation_name, section=None, **kwargs):
#         self.relation_name = relation_name
#         self.section = section
#         super().__init__(**kwargs)

#     def get_attribute(self, instance):
#         return instance

#     def to_representation(self, instance):
#         return [
#             {
#                 "id": item.pk,
#                 "key": item.key,
#                 "value": item.value,
#             }
#             for item in getattr(
#                 instance,
#                 self.relation_name
#             ).all()
#             if (
#                 self.section is None
#                 or getattr(item, "section", None) == self.section
#             )
#         ]

#     def to_internal_value(self, data):

#         if isinstance(data, str):
#             try:
#                 data = json.loads(data)
#             except json.JSONDecodeError:
#                 raise serializers.ValidationError(
#                     "Expected a JSON array of {key, value} objects."
#                 )

#         if not isinstance(data, list):
#             raise serializers.ValidationError(
#                 "Expected a list of {key, value} objects."
#             )

#         result = []

#         for item in data:

#             if (
#                 not isinstance(item, dict)
#                 or not str(item.get("key", "")).strip()
#             ):
#                 raise serializers.ValidationError(
#                     "Each item must contain a non-empty key and a value."
#                 )

#             result.append(
#                 {
#                     "key": str(item["key"]).strip(),
#                     "value": str(item.get("value", "")),
#                 }
#             )

#         return result


# # ============================================================
# # BUSINESS DOCUMENT
# # ============================================================

# class BusinessDocumentSerializer(serializers.ModelSerializer):

#     class Meta:
#         model = BusinessDocument

#         fields = (
#             "id",
#             "business",
#             "title",
#             "file",
#             "uploaded_by",
#             "created_at",
#         )

#         read_only_fields = (
#             "uploaded_by",
#             "created_at",
#         )


# class BusinessSerializer(serializers.ModelSerializer):

#     login_username = serializers.CharField(
#         write_only=True,
#         required=False
#     )

#     login_password = serializers.CharField(
#         write_only=True,
#         required=False,
#         min_length=8
#     )

#     # Business Email
#     email = serializers.EmailField(
#         write_only=True,
#         required=True
#     )

#     business_email = serializers.SerializerMethodField(
#         read_only=True
#     )

#     # Bank Details
#     bank_details = KeyValueRelationField(
#         "bank_detail_fields",
#         required=False
#     )

#     # Business Details
#     business_details = KeyValueRelationField(
#         "detail_fields",
#         required=False
#     )

#     documents = BusinessDocumentSerializer(
#         source="uploaded_documents",
#         many=True,
#         read_only=True
#     )

#     investor_count = serializers.IntegerField(
#         read_only=True
#     )

#     total_investment = serializers.SerializerMethodField()

#     class Meta:
#         model = Business

#         fields = (
#             "id",

#             # Login
#             "login_username",
#             "login_password",

#             # Business Email
#             "email",
#             "business_email",

#             # Business
#             "name",
#             "location",
#             "owner_name",
#             "owner_aadhar",
#             "owner_pan",
#             "owner_mobile",
#             "owner_sex",

#             # Relations
#             "bank_details",
#             "business_details",
#             "owner_photo",
#             "business_photo",
#             "documents",

#             # Statistics
#             "investor_count",
#             "total_investment",

#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "created_at",
#             "updated_at",
#             "business_email",
#         )

#     def get_business_email(self, obj):
#         if obj.user:
#             return obj.user.email or ""
#         return ""

#     def get_total_investment(self, obj):

#         one_time = (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True,
#                 amount__isnull=False
#             )
#             .aggregate(
#                 v=Sum("amount")
#             )["v"]
#             or Decimal("0")
#         )

#         recurring = (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True,
#                 scheme__category=Scheme.Categories.PLATINUM,
#                 recurring_contributions__deleted_at__isnull=True,
#             )
#             .aggregate(
#                 v=Sum("recurring_contributions__amount")
#             )["v"]
#             or Decimal("0")
#         )

#         return one_time + recurring

#     def _replace_kv(
#         self,
#         business,
#         bank_details=None,
#         business_details=None
#     ):

#         if bank_details is not None:

#             BusinessBankDetail.objects.filter(
#                 business=business
#             ).delete()

#             BusinessBankDetail.objects.bulk_create(
#                 [
#                     BusinessBankDetail(
#                         business=business,
#                         key=x["key"],
#                         value=x["value"],
#                     )
#                     for x in bank_details
#                 ]
#             )

#         if business_details is not None:

#             BusinessDetailField.objects.filter(
#                 business=business
#             ).delete()

#             BusinessDetailField.objects.bulk_create(
#                 [
#                     BusinessDetailField(
#                         business=business,
#                         key=x["key"],
#                         value=x["value"],
#                     )
#                     for x in business_details
#                 ]
#             )

#     def create(self, validated_data):

#         username = validated_data.pop(
#             "login_username",
#             None
#         )

#         password = validated_data.pop(
#             "login_password",
#             None
#         )

#         email = validated_data.pop(
#             "email"
#         )

#         bank_details = validated_data.pop(
#             "bank_details",
#             []
#         )

#         business_details = validated_data.pop(
#             "business_details",
#             []
#         )

#         request = self.context.get("request")

#         if not username:
#             raise serializers.ValidationError({
#                 "login_username": "Login Username is required."
#             })

#         if not password:
#             raise serializers.ValidationError({
#                 "login_password": "Login Password is required."
#             })

#         # Prevent duplicate email
#         if User.objects.filter(
#             email__iexact=email
#         ).exists():
#             raise serializers.ValidationError({
#                 "email": "This email is already registered."
#             })

#         # Prevent duplicate username
#         if User.objects.filter(
#             username__iexact=username
#         ).exists():
#             raise serializers.ValidationError({
#                 "login_username": "This username is already registered."
#             })

#         user = User.objects.create_user(
#             username=username,
#             email=email,
#             password=password,
#             role=User.Roles.BUSINESS,
#             phone=validated_data.get(
#                 "owner_mobile",
#                 ""
#             ),
#         )

#         validated_data["created_by"] = request.user
#         validated_data["user"] = user

#         business = Business.objects.create(
#             **validated_data
#         )

#         self._replace_kv(
#             business,
#             bank_details,
#             business_details
#         )

#         return business

#     def update(self, instance, validated_data):

#         validated_data.pop(
#             "login_username",
#             None
#         )

#         password = validated_data.pop(
#             "login_password",
#             None
#         )

#         email = validated_data.pop(
#             "email",
#             None
#         )

#         bank_details = validated_data.pop(
#             "bank_details",
#             None
#         )

#         business_details = validated_data.pop(
#             "business_details",
#             None
#         )

#         # Update Business Email
#         if email is not None:

#             existing_user = (
#                 User.objects
#                 .filter(
#                     email__iexact=email
#                 )
#                 .exclude(
#                     pk=instance.user_id
#                 )
#                 .first()
#             )

#             if existing_user:
#                 raise serializers.ValidationError({
#                     "email": "This email is already registered."
#                 })

#             if instance.user:
#                 instance.user.email = email
#                 instance.user.save(
#                     update_fields=["email"]
#                 )

#         # Update password if provided
#         if password:

#             if not instance.user:
#                 raise serializers.ValidationError({
#                     "login_password":
#                         "Business login account does not exist."
#                 })

#             instance.user.set_password(password)

#             instance.user.save(
#                 update_fields=["password"]
#             )

#         instance = super().update(
#             instance,
#             validated_data
#         )

#         self._replace_kv(
#             instance,
#             bank_details,
#             business_details
#         )

#         return instance
# class AccountantDocumentSerializer(serializers.ModelSerializer):
#     class Meta:
#         model = AccountantDocument
#         fields = ("id", "title", "file", "uploaded_by", "created_at")
#         read_only_fields = ("uploaded_by", "created_at")


# # ============================================================
# # INVESTOR DOCUMENT
# # ============================================================

# class InvestorDocumentSerializer(serializers.ModelSerializer):

#     class Meta:
#         model = InvestorDocument

#         fields = (
#             "id",
#             "section",
#             "title",
#             "file",
#             "uploaded_by",
#             "created_at",
#         )

#         read_only_fields = (
#             "uploaded_by",
#             "created_at",
#         )


# # ============================================================
# # INVESTOR DETAIL FIELD
# # ============================================================

# class InvestorDetailFieldSerializer(serializers.ModelSerializer):

#     class Meta:
#         model = InvestorDetailField

#         fields = (
#             "id",
#             "section",
#             "key",
#             "value",
#         )


# # ============================================================
# # INVESTOR
# # ============================================================

# class InvestorSerializer(serializers.ModelSerializer):

#     login_username = serializers.CharField(
#         write_only=True,
#         required=False
#     )

#     login_username_display = serializers.SerializerMethodField()

#     login_password_status = serializers.SerializerMethodField()

#     login_password = serializers.CharField(
#         write_only=True,
#         required=False,
#         min_length=8
#     )

#     identity_details = KeyValueRelationField(
#         "detail_fields",
#         section="IDENTITY",
#         required=False
#     )

#     bank_details = KeyValueRelationField(
#         "detail_fields",
#         section="BANK",
#         required=False
#     )

#     nominee_details = KeyValueRelationField(
#         "detail_fields",
#         section="NOMINEE",
#         required=False
#     )

#     identity_fields = serializers.SerializerMethodField()
#     bank_fields = serializers.SerializerMethodField()
#     nominee_fields = serializers.SerializerMethodField()

#     documents = InvestorDocumentSerializer(
#         source="uploaded_documents",
#         many=True,
#         read_only=True
#     )

#     total_investment = serializers.SerializerMethodField()

#     business_count = serializers.IntegerField(
#         read_only=True
#     )

#     class Meta:
#         model = Investor

#         fields = (
#             "id",
#             "login_username",
#             "login_password",
#             "login_username_display",
#             "login_password_status",
#             "user",
#             "name",
#             "email",
#             "mobile",
#             "image",
#             "identity_details",
#             "bank_details",
#             "nominee_details",
#             "identity_fields",
#             "bank_fields",
#             "nominee_fields",
#             "documents",
#             "other_details",
#             "total_investment",
#             "business_count",
#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "created_by",
#             "created_at",
#             "updated_at",
#         )

#     def get_login_username_display(self, obj):
#         return obj.user.username if obj.user else "—"

#     def get_login_password_status(self, obj):

#         return (
#             "Password set (hidden for security)"
#             if obj.user and obj.user.has_usable_password()
#             else "No password set"
#         )

#     def get_total_investment(self, obj):

#         one_time = (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True,
#                 amount__isnull=False
#             )
#             .aggregate(
#                 v=Sum("amount")
#             )["v"]
#             or Decimal("0")
#         )

#         recurring = (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True,
#                 scheme__category=Scheme.Categories.PLATINUM,
#                 recurring_contributions__deleted_at__isnull=True,
#             )
#             .aggregate(
#                 v=Sum(
#                     "recurring_contributions__amount"
#                 )
#             )["v"]
#             or Decimal("0")
#         )

#         return one_time + recurring

#     def _section(self, investor, section):

#         return list(
#             investor.detail_fields
#             .filter(section=section)
#             .values(
#                 "id",
#                 "key",
#                 "value"
#             )
#         )

#     def get_identity_fields(self, obj):
#         return self._section(
#             obj,
#             InvestorDetailField.Sections.IDENTITY
#         )

#     def get_bank_fields(self, obj):
#         return self._section(
#             obj,
#             InvestorDetailField.Sections.BANK
#         )

#     def get_nominee_fields(self, obj):
#         return self._section(
#             obj,
#             InvestorDetailField.Sections.NOMINEE
#         )

#     def _replace_section(
#         self,
#         investor,
#         section,
#         values
#     ):

#         InvestorDetailField.objects.filter(
#             investor=investor,
#             section=section
#         ).delete()

#         InvestorDetailField.objects.bulk_create(
#             [
#                 InvestorDetailField(
#                     investor=investor,
#                     section=section,
#                     key=x["key"],
#                     value=x["value"],
#                 )
#                 for x in values
#             ]
#         )

#     def _save_files(
#         self,
#         investor,
#         request
#     ):

#         if not request:
#             return

#         mapping = {
#             "identity_files":
#                 InvestorDocument.Sections.IDENTITY,

#             "bank_files":
#                 InvestorDocument.Sections.BANK,

#             "nominee_files":
#                 InvestorDocument.Sections.NOMINEE,
#         }

#         for input_name, section in mapping.items():

#             titles = (
#                 request.data.getlist(
#                     f"{input_name}_titles"
#                 )
#                 if hasattr(request.data, "getlist")
#                 else []
#             )

#             for index, file in enumerate(
#                 request.FILES.getlist(input_name)
#             ):

#                 title = (
#                     titles[index]
#                     if index < len(titles)
#                     else file.name
#                 )

#                 InvestorDocument.objects.create(
#                     investor=investor,
#                     section=section,
#                     title=title or file.name,
#                     file=file,
#                     uploaded_by=request.user,
#                 )

#     def create(self, validated_data):

#         username = validated_data.pop(
#             "login_username",
#             None
#         )

#         password = validated_data.pop(
#             "login_password",
#             None
#         )

#         identity = validated_data.pop(
#             "identity_details",
#             []
#         )

#         bank = validated_data.pop(
#             "bank_details",
#             []
#         )

#         nominee = validated_data.pop(
#             "nominee_details",
#             []
#         )

#         request = self.context["request"]

#         user = None

#         if username and password:

#             user = User.objects.create_user(
#                 username=username,
#                 password=password,
#                 email=(
#                     validated_data.get("email")
#                     or f"{username}@investor.local"
#                 ),
#                 role=User.Roles.INVESTOR,
#                 phone=validated_data.get(
#                     "mobile",
#                     ""
#                 ),
#             )

#         validated_data["created_by"] = request.user
#         validated_data["user"] = user

#         investor = super().create(
#             validated_data
#         )

#         self._replace_section(
#             investor,
#             InvestorDetailField.Sections.IDENTITY,
#             identity
#         )

#         self._replace_section(
#             investor,
#             InvestorDetailField.Sections.BANK,
#             bank
#         )

#         self._replace_section(
#             investor,
#             InvestorDetailField.Sections.NOMINEE,
#             nominee
#         )

#         self._save_files(
#             investor,
#             request
#         )

#         return investor

#     def update(self, instance, validated_data):

#         validated_data.pop(
#             "login_username",
#             None
#         )

#         password = validated_data.pop(
#             "login_password",
#             None
#         )

#         identity = validated_data.pop(
#             "identity_details",
#             None
#         )

#         bank = validated_data.pop(
#             "bank_details",
#             None
#         )

#         nominee = validated_data.pop(
#             "nominee_details",
#             None
#         )

#         request = self.context.get(
#             "request"
#         )

#         instance = super().update(
#             instance,
#             validated_data
#         )

#         if password and instance.user:

#             instance.user.set_password(
#                 password
#             )

#             instance.user.save(
#                 update_fields=["password"]
#             )

#         if identity is not None:
#             self._replace_section(
#                 instance,
#                 InvestorDetailField.Sections.IDENTITY,
#                 identity
#             )

#         if bank is not None:
#             self._replace_section(
#                 instance,
#                 InvestorDetailField.Sections.BANK,
#                 bank
#             )

#         if nominee is not None:
#             self._replace_section(
#                 instance,
#                 InvestorDetailField.Sections.NOMINEE,
#                 nominee
#             )

#         self._save_files(
#             instance,
#             request
#         )

#         return instance


# # ============================================================
# # SCHEME
# # ============================================================

# class SchemeSerializer(serializers.ModelSerializer):

#     scheme_display_name = serializers.SerializerMethodField()

#     class Meta:
#         model = Scheme

#         fields = (
#             "id",
#             "category",
#             "name",
#             "description",
#             "tenure_months",
#             "annual_interest_rate",
#             "penalty_rate",
#             "withdrawal_type",
#             "compounding_frequency",
#             "rules",
#             "is_active",
#             "scheme_display_name",
#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "created_by",
#             "created_at",
#             "updated_at",
#             "scheme_display_name",
#         )

#     def get_scheme_display_name(self, obj):

#         return (
#             f"{obj.get_category_display()}-"
#             f"{obj.name}-"
#             f"{obj.tenure_months} months-"
#             f"{obj.annual_interest_rate}% p.a."
#         )

#     # def validate(self, attrs):

#     #     category = attrs.get(
#     #         "category",
#     #         getattr(
#     #             self.instance,
#     #             "category",
#     #             None
#     #         )
#     #     )

#     #     if (
#     #         category == Scheme.Categories.SILVER
#     #         and not attrs.get(
#     #             "withdrawal_type",
#     #             getattr(
#     #                 self.instance,
#     #                 "withdrawal_type",
#     #                 ""
#     #             )
#     #         )
#     #     ):
#     #         raise serializers.ValidationError(
#     #             {
#     #                 "withdrawal_type":
#     #                     "Required for Silver."
#     #             }
#     #         )

#     #     if (
#     #         category == Scheme.Categories.GOLD
#     #         and not attrs.get(
#     #             "compounding_frequency",
#     #             getattr(
#     #                 self.instance,
#     #                 "compounding_frequency",
#     #                 ""
#     #             )
#     #         )
#     #     ):
#     #         attrs["compounding_frequency"] = "MONTHLY"

#     #     if category == Scheme.Categories.PLATINUM:
#     #         # Platinum has no amount field on Scheme.
#     #         pass

#     #     if category == Scheme.Categories.DIAMOND:
#     #         attrs["penalty_rate"] = Decimal("0")
#     #         attrs["compounding_frequency"] = ""

#     #     return attrs
#     def validate(self, attrs):

#         category = attrs.get(
#             "category",
#             getattr(self.instance, "category", None)
#         )
    
#         if not attrs.get(
#             "withdrawal_type",
#             getattr(self.instance, "withdrawal_type", "")
#         ):
#             attrs["withdrawal_type"] = "MONTHLY_WITHDRAWAL"
    
#         if (
#             category == Scheme.Categories.GOLD
#             and not attrs.get(
#                 "compounding_frequency",
#                 getattr(self.instance, "compounding_frequency", "")
#             )
#         ):
#             attrs["compounding_frequency"] = "MONTHLY"
    
#         if category == Scheme.Categories.PLATINUM:
#             pass
    
#         if category == Scheme.Categories.DIAMOND:
#             attrs["penalty_rate"] = Decimal("0")
#             attrs["compounding_frequency"] = ""
    
#         return attrs
#     def to_representation(self, instance):
#         data = super().to_representation(instance)
#         if instance.category == Scheme.Categories.DIAMOND:
#             data["penalty_rate"] = "0.0000"
#             data["withdrawal_type"] = ""
#             data["compounding_frequency"] = ""
#         return data


# # ============================================================
# # KUBER → BUSINESS INVESTMENT
# # ============================================================

# from rest_framework import serializers


# class KuberBusinessInvestmentSerializer(
#     serializers.ModelSerializer
# ):
#     business_name = serializers.CharField(
#         source="business.name",
#         read_only=True
#     )

#     interest = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )

#     current_principal = serializers.SerializerMethodField()

#     class Meta:
#         model = KuberBusinessInvestment

#         fields = (
#             "id",
#             "business",
#             "business_name",
#             "investment_type",
#             "amount",
#             "annual_rate",
#             "equity_share_percentage",
#             "current_principal",
#             "investment_date",
#             "maturity_date",
#             "interest",
#             "notes",
#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "current_principal",
#             "interest",
#             "created_at",
#             "updated_at",
#         )

#     def get_current_principal(self, obj):
#         try:
#             from .services import kuber_equity_current_principal
#             return kuber_equity_current_principal(obj)
#         except (ValueError, TypeError):
#             return None if obj.investment_type == KuberBusinessInvestment.InvestmentTypes.EQUITY_SHARE else obj.amount

#     def validate(self, attrs):
#         from .models import KuberBusinessInvestment

#         investment_type = attrs.get(
#             "investment_type",
#             getattr(
#                 self.instance,
#                 "investment_type",
#                 KuberBusinessInvestment.InvestmentTypes.ORDINARY,
#             ),
#         )

#         amount = attrs.get("amount", getattr(self.instance, "amount", None))
#         if amount is None or Decimal(amount) <= 0:
#             raise serializers.ValidationError({"amount": "Investment amount must be greater than zero."})

#         investment_date = attrs.get("investment_date", getattr(self.instance, "investment_date", None))
#         maturity_date = attrs.get("maturity_date", getattr(self.instance, "maturity_date", None))
#         if self.instance is None and maturity_date is None:
#             raise serializers.ValidationError({"maturity_date": "Maturity date is required for Kuber Business investment."})
#         if maturity_date is not None and investment_date is not None and maturity_date < investment_date:
#             raise serializers.ValidationError({"maturity_date": "Maturity date cannot be before the investment date."})

#         if investment_type == KuberBusinessInvestment.InvestmentTypes.EQUITY_SHARE:
#             attrs["annual_rate"] = Decimal("0")

#             share = attrs.get(
#                 "equity_share_percentage",
#                 getattr(self.instance, "equity_share_percentage", None),
#             )
#             if share is None:
#                 raise serializers.ValidationError({
#                     "equity_share_percentage": "Share percentage is required for Equity Share investment."
#                 })
#             share = Decimal(share)
#             if share <= 0 or share > 100:
#                 raise serializers.ValidationError({
#                     "equity_share_percentage": "Share percentage must be greater than 0 and cannot exceed 100."
#                 })
#             attrs["equity_share_percentage"] = share

#             # Share percentage is stored only for display/reference. It is
#             # deliberately not used in any financial calculation.

#             # Keep the entered share percentage locked after creation.
#             if self.instance is not None:
#                 immutable = {"business", "investment_type", "amount", "investment_date", "equity_share_percentage"}
#                 changed = {
#                     field for field in immutable
#                     if field in attrs and attrs[field] != getattr(self.instance, field)
#                 }
#                 if changed:
#                     raise serializers.ValidationError({
#                         field: "Equity Share investment terms are locked after creation."
#                         for field in sorted(changed)
#                     })
#         else:
#             attrs["equity_share_percentage"] = None
#         return attrs

#     def create(self, validated_data):
#         from .services import create_kuber_business_investment
    
#         created_by = validated_data.pop("created_by", None)
    
#         return create_kuber_business_investment(
#             business=validated_data["business"],
#             investment_type=validated_data.get(
#                 "investment_type",
#                 KuberBusinessInvestment.InvestmentTypes.ORDINARY,
#             ),
#             amount=validated_data["amount"],
#             annual_rate=validated_data.get("annual_rate", Decimal("0")),
#             equity_share_percentage=validated_data.get("equity_share_percentage"),
#             investment_date=validated_data.get("investment_date"),
#             maturity_date=validated_data.get("maturity_date"),
#             notes=validated_data.get("notes", ""),
#             created_by=created_by,
#         )


# # ============================================================
# # INVESTMENT
# # ============================================================

# class InvestmentSerializer(
#     serializers.ModelSerializer
# ):

#     investor_name = serializers.CharField(
#         source="investor.name",
#         read_only=True
#     )

#     business_name = serializers.CharField(
#         source="business.name",
#         read_only=True
#     )

#     scheme_name = serializers.CharField(
#         source="scheme.name",
#         read_only=True
#     )

#     scheme_display_name = serializers.SerializerMethodField()
#     scheme_category = serializers.SerializerMethodField()
#     withdrawal_type = serializers.SerializerMethodField()
#     scheme_interest = serializers.SerializerMethodField()
#     tenure_months = serializers.SerializerMethodField()
#     maturity_amount = serializers.SerializerMethodField()
#     premature_amount = serializers.SerializerMethodField()
#     interest_earned_till_date = serializers.SerializerMethodField()
#     total_contributed = serializers.SerializerMethodField()
#     terms_display = serializers.SerializerMethodField()
#     diamond_valuation_before_investment = serializers.SerializerMethodField()
#     diamond_valuation_after_investment = serializers.SerializerMethodField()
#     diamond_ownership_percentage = serializers.SerializerMethodField()
#     diamond_maturity_valuation = serializers.SerializerMethodField()
#     current_principal = serializers.SerializerMethodField()

#     class Meta:
#         model = Investment

#         fields = (
#             "id",
#             "investor",
#             "investor_name",
#             "business",
#             "business_name",
#             "scheme",
#             "scheme_name",
#             "scheme_display_name",
#             "scheme_category",
#             "withdrawal_type",
#             "scheme_interest",
#             "tenure_months",
#             "amount",
#             "monthly_installment",
#             "investor_annual_rate",
#             "investment_date",
#             "maturity_date",
#             "status",
#             "terms_snapshot",
#             "total_contributed",
#             "maturity_amount",
#             "premature_amount",
#             "interest_earned_till_date",
#             "terms_display",
#             "diamond_valuation_before_investment",
#             "diamond_valuation_after_investment",
#             "diamond_ownership_percentage",
#             "diamond_maturity_valuation",
#             "current_principal",
#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "created_by",
#             "maturity_date",
#             "terms_snapshot",
#             "investor_annual_rate",
#             "created_at",
#             "updated_at",
#             "maturity_amount",
#             "premature_amount",
#             "total_contributed",
#             "scheme_display_name",
#             "interest_earned_till_date",
#             "terms_display",
#             "diamond_valuation_before_investment",
#             "diamond_valuation_after_investment",
#             "diamond_ownership_percentage",
#             "diamond_maturity_valuation",
#             "current_principal",
#         )

#     def get_diamond_valuation_before_investment(self, obj):
#         return (obj.terms_snapshot or {}).get("diamond_valuation_before_investment")

#     def get_diamond_valuation_after_investment(self, obj):
#         return (obj.terms_snapshot or {}).get("diamond_valuation_after_investment")

#     def get_diamond_ownership_percentage(self, obj):
#         return (obj.terms_snapshot or {}).get("diamond_ownership_percentage")

#     def get_diamond_maturity_valuation(self, obj):
#         if obj.scheme.category != Scheme.Categories.DIAMOND:
#             return None
#         try:
#             from .services import diamond_maturity_valuation
#             return diamond_maturity_valuation(obj, as_of=obj.maturity_date)
#         except (ValueError, TypeError):
#             return None

#     def get_current_principal(self, obj):
#         try:
#             from .services import investment_current_principal
#             return investment_current_principal(obj)
#         except (ValueError, TypeError):
#             return None if obj.scheme.category == Scheme.Categories.DIAMOND else (obj.amount or Decimal("0"))

#     def get_scheme_display_name(self, obj):

#         return (
#             f"{obj.scheme.get_category_display()}-"
#             f"{obj.scheme.name}-"
#             f"{obj.scheme.tenure_months} months-"
#             f"{obj.scheme.annual_interest_rate}% p.a."
#         )

#     def get_scheme_category(self, obj):
#         return obj.scheme.get_category_display()

#     def get_withdrawal_type(self, obj):
#         return obj.scheme.withdrawal_type or "—"

#     def get_scheme_interest(self, obj):
#         return obj.scheme.annual_interest_rate

#     def get_tenure_months(self, obj):
#         return obj.scheme.tenure_months

#     def get_terms_display(self, obj):

#         scheme = obj.scheme

#         if scheme.category == Scheme.Categories.DIAMOND:
#             ownership = (obj.terms_snapshot or {}).get("diamond_ownership_percentage")
#             return (
#                 f"Category - {scheme.get_category_display()}, "
#                 f"Tenure - {scheme.tenure_months} Months, "
#                 f"Interest Percentage - {scheme.annual_interest_rate}% p.a., "
#                 f"Maturity - Business Total Equity × {ownership or 'locked ownership'}%, "
#                 f"Premature Withdrawal - Not Available"
#             )

#         return (
#             f"Category - {scheme.get_category_display()}, "
#             f"Tenure - {scheme.tenure_months} Months, "
#             f"Interest Percentage - {scheme.annual_interest_rate}% p.a., "
#             f"Penalty - {scheme.penalty_rate}%"
#         )

#     def get_interest_earned_till_date(self, obj):
#         return interest_till_date(obj)

#     def get_total_contributed(self, obj):

#         if obj.scheme.category == Scheme.Categories.PLATINUM:

#             return (
#                 obj.recurring_contributions
#                 .aggregate(
#                     v=Sum("amount")
#                 )["v"]
#                 or Decimal("0")
#             )

#         return obj.amount or Decimal("0")

#     def get_maturity_amount(self, obj):

#         try:
#             return maturity_value(obj)

#         except ValueError:
#             return None

#     def get_premature_amount(self, obj):

#         try:
#             return premature_value(obj)

#         except ValueError:
#             return None

#     def validate(self, attrs):

#         scheme = attrs.get(
#             "scheme",
#             getattr(
#                 self.instance,
#                 "scheme",
#                 None
#             )
#         )

#         amount = attrs.get(
#             "amount",
#             getattr(
#                 self.instance,
#                 "amount",
#                 None
#             )
#         )

#         monthly = attrs.get(
#             "monthly_installment",
#             getattr(
#                 self.instance,
#                 "monthly_installment",
#                 None
#             )
#         )

#         if scheme:

#             if scheme.category == Scheme.Categories.PLATINUM:

#                 if monthly is None:

#                     raise serializers.ValidationError(
#                         {
#                             "monthly_installment":
#                                 "Required for Platinum investment."
#                         }
#                     )

#                 attrs["amount"] = None

#             else:

#                 if amount is None:

#                     raise serializers.ValidationError(
#                         {
#                             "amount":
#                                 "Required for Silver, Gold and Diamond investment."
#                         }
#                     )

#                 attrs["monthly_installment"] = None

#         return attrs

#     def create(self, validated_data):

#         from .services import (
#             create_investment_with_snapshot
#         )

#         request = self.context["request"]

#         scheme = validated_data["scheme"]

#         return create_investment_with_snapshot(
#             investor=validated_data["investor"],
#             business=validated_data["business"],
#             scheme=scheme,
#             amount=validated_data.get("amount"),
#             monthly_installment=validated_data.get(
#                 "monthly_installment"
#             ),
#             created_by=request.user,
#             investment_date=validated_data.get(
#                 "investment_date"
#             ),
#         )

#     def update(self, instance, validated_data):

#         from .services import (
#             months_after,
#             build_terms_snapshot
#         )

#         scheme = validated_data.get(
#             "scheme",
#             instance.scheme
#         )

#         amount = validated_data.get(
#             "amount",
#             instance.amount
#         )

#         monthly = validated_data.get(
#             "monthly_installment",
#             instance.monthly_installment
#         )

#         if scheme.category == Scheme.Categories.PLATINUM:

#             amount = None

#         else:

#             monthly = None

#         instance = super().update(
#             instance,
#             {
#                 **validated_data,
#                 "amount": amount,
#                 "monthly_installment": monthly,
#                 "investor_annual_rate":
#                     scheme.annual_interest_rate,
#             }
#         )

#         instance.maturity_date = months_after(
#             instance.investment_date,
#             scheme.tenure_months
#         )

#         instance.terms_snapshot = build_terms_snapshot(
#             scheme,
#             instance
#         )

#         instance.save(
#             update_fields=[
#                 "maturity_date",
#                 "terms_snapshot",
#                 "updated_at",
#                 "investor_annual_rate",
#             ]
#         )

#         return instance


# # ============================================================
# # BALANCE SHEET
# # ============================================================

# class BalanceSheetSerializer(serializers.ModelSerializer):
#     business_name = serializers.CharField(
#         source="business.name",
#         read_only=True
#     )

#     # Show the username instead of the User ID
#     entered_by = serializers.CharField(
#         source="entered_by.username",
#         read_only=True
#     )

#     total_current_assets = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )
#     total_non_current_assets = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )
#     total_assets = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )

#     total_current_liabilities = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )
#     total_non_current_liabilities = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )
#     total_liabilities = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )

#     total_equity = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )
#     liabilities_and_equity = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )
#     balance_difference = serializers.DecimalField(
#         max_digits=14,
#         decimal_places=2,
#         read_only=True
#     )

#     is_balanced = serializers.BooleanField(read_only=True)

#     class Meta:
#         model = BalanceSheet

#         fields = (
#             "id",
#             "business",
#             "business_name",
#             "statement_date",

#             # Current Assets
#             "cash_in_hand",
#             "bank_balance",
#             "accounts_receivable",
#             "inventory",
#             "prepaid_expenses",
#             "other_current_assets",

#             # Non-Current Assets
#             "fixed_assets",
#             "accumulated_depreciation",
#             "other_non_current_assets",

#             # Current Liabilities
#             "accounts_payable",
#             "short_term_borrowings",
#             "accrued_expenses",
#             "taxes_payable",
#             "other_current_liabilities",

#             # Non-Current Liabilities
#             "long_term_loans",
#             "other_non_current_liabilities",

#             # Equity
#             "owner_capital",
#             "reserves_surplus",
#             "retained_earnings",
#             "drawings",

#             "remarks",

#             # Calculated fields
#             "total_current_assets",
#             "total_non_current_assets",
#             "total_assets",
#             "total_current_liabilities",
#             "total_non_current_liabilities",
#             "total_liabilities",
#             "total_equity",
#             "liabilities_and_equity",
#             "balance_difference",
#             "is_balanced",

#             # User who entered the balance sheet
#             "entered_by",

#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "entered_by",
#             "created_at",
#             "updated_at",
#         )

#     def validate(self, attrs):
#         # Decimal fields reject invalid/negative values at model level.
#         # Totals are calculated by the backend.
#         # An unbalanced statement can still be saved and is explicitly flagged.
#         return attrs


# # ============================================================
# # DAILY FINANCIAL STATEMENT / P&L
# # ============================================================

# class DailyFinancialStatementSerializer(serializers.ModelSerializer):
#     business_name = serializers.CharField(source="business.name", read_only=True)
#     submitted_by = serializers.CharField(source="submitted_by.username", read_only=True)
#     reviewed_by = serializers.CharField(source="reviewed_by.username", read_only=True, allow_null=True)

#     gross_profit = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     operating_profit = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     profit_before_tax = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     net_profit = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     profit_margin = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     financial_status = serializers.CharField(read_only=True)
#     operating_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     investing_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     financing_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
#     net_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)

#     class Meta:
#         model = DailyFinancialStatement
#         fields = (
#             "id", "business", "business_name", "statement_date",
#             "revenue", "direct_cost", "operating_expenses", "other_income",
#             "finance_cost", "tax", "gross_profit", "operating_profit",
#             "profit_before_tax", "net_profit", "profit_margin", "financial_status",
#             "operating_cash_inflow", "operating_cash_outflow",
#             "investing_cash_inflow", "investing_cash_outflow",
#             "financing_cash_inflow", "financing_cash_outflow",
#             "operating_cash_flow", "investing_cash_flow", "financing_cash_flow",
#             "net_cash_flow", "remarks", "status", "submitted_by", "submitted_at",
#             "reviewed_by", "reviewed_at", "created_at", "updated_at",
#         )
#         read_only_fields = (
#             "submitted_by", "submitted_at", "reviewed_by", "reviewed_at",
#             "created_at", "updated_at", "gross_profit", "operating_profit",
#             "profit_before_tax", "net_profit", "profit_margin", "financial_status",
#             "operating_cash_flow", "investing_cash_flow", "financing_cash_flow",
#             "net_cash_flow",
#         )

#     def validate(self, attrs):
#         for field in (
#             "revenue", "direct_cost", "operating_expenses", "other_income",
#             "finance_cost", "tax", "operating_cash_inflow", "operating_cash_outflow",
#             "investing_cash_inflow", "investing_cash_outflow",
#             "financing_cash_inflow", "financing_cash_outflow",
#         ):
#             value = attrs.get(field)
#             if value is not None and value < 0:
#                 raise serializers.ValidationError({field: "This value cannot be negative."})
#         return attrs


# class InvestorBusinessPerformanceSerializer(serializers.Serializer):
#     """Investor-safe view; never exposes assets, liabilities, equity or cash balances."""
#     business_id = serializers.IntegerField()
#     business_name = serializers.CharField()
#     period = serializers.DateField(source="statement_date")
#     status = serializers.CharField(source="financial_status")
#     net_profit = serializers.DecimalField(max_digits=14, decimal_places=2)
#     profit_margin = serializers.DecimalField(max_digits=14, decimal_places=2)
#     updated_at = serializers.DateTimeField()
#     change_from_previous_period = serializers.DecimalField(
#         max_digits=14, decimal_places=2, allow_null=True
#     )


# # ============================================================
# # LEDGER
# # ============================================================

# class LedgerEntrySerializer(serializers.ModelSerializer):

#     investor_name = serializers.CharField(
#         source="investor.name",
#         read_only=True
#     )

#     business_name = serializers.CharField(
#         source="business.name",
#         read_only=True
#     )

#     scheme = serializers.SerializerMethodField()

#     class Meta:
#         model = LedgerEntry

#         fields = (
#             "id",
#             "business",
#             "business_name",
#             "investment",
#             "investor",
#             "investor_name",
#             "scheme",
#             "kuber_investment",
#             "entry_date",
#             "entry_type",
#             "amount",
#             "annual_rate",
#             "description",
#             "reference",
#             "created_at",
#         )

#         read_only_fields = (
#             "created_at",
#             "scheme",
#         )

#     def get_scheme(self, obj):
#         if not obj.investment or not obj.investment.scheme:
#             return None

#         scheme = obj.investment.scheme

#         return {
#             "category": scheme.get_category_display(),
#             "name": scheme.name,
#             "tenure_months": scheme.tenure_months,
#             "annual_interest_rate": scheme.annual_interest_rate,
#             "display": (
#                 f"{scheme.get_category_display()} - "
#                 f"{scheme.name} - "
#                 f"{scheme.tenure_months} Months - "
#                 f"{scheme.annual_interest_rate}% p.a."
#             ),
#         }


# # ============================================================
# # PAYOUT
# # ============================================================

# class PayoutSerializer(
#     serializers.ModelSerializer
# ):

#     investor_name = serializers.CharField(
#         source="investor.name",
#         read_only=True
#     )

#     class Meta:
#         model = Payout

#         fields = (
#             "id",
#             "investment",
#             "investor",
#             "investor_name",
#             "amount",
#             "payout_date",
#             "notes",
#             "created_by",
#             "created_at",
#         )

#         read_only_fields = (
#             "investor",
#             "created_by",
#             "created_at",
#         )

#     def validate(self, attrs):

#         investment = attrs["investment"]

#         if investment.status in (
#             Investment.Status.CANCELLED,
#             Investment.Status.CLOSED,
#         ):

#             raise serializers.ValidationError(
#                 "Cannot create payout for a closed/cancelled investment."
#             )

#         attrs["investor"] = investment.investor

#         return attrs


# # ============================================================
# # RECURRING CONTRIBUTION
# # ============================================================

# class RecurringContributionSerializer(
#     serializers.ModelSerializer
# ):

#     class Meta:
#         model = RecurringContribution

#         fields = (
#             "id",
#             "investment",
#             "contribution_date",
#             "amount",
#             "created_by",
#             "created_at",
#         )

#         read_only_fields = (
#             "created_by",
#             "created_at",
#         )

#     def validate(self, attrs):

#         investment = self.context.get(
#             "investment"
#         )

#         if (
#             investment
#             and investment.scheme.category
#             != Scheme.Categories.PLATINUM
#         ):

#             raise serializers.ValidationError(
#                 "Recurring contributions are only for Platinum investments."
#             )

#         return attrs


# # ============================================================
# # CERTIFICATE ATTACHMENT
# # ============================================================

# class CertificateAttachmentSerializer(
#     serializers.ModelSerializer
# ):

#     class Meta:
#         model = CertificateAttachment

#         fields = (
#             "id",
#             "title",
#             "file",
#             "sort_order",
#             "created_at",
#         )

#         read_only_fields = (
#             "created_at",
#         )


# # ============================================================
# # CERTIFICATE
# # ============================================================

# class CertificateSerializer(
#     serializers.ModelSerializer
# ):

#     investor_name = serializers.CharField(
#         source="investment.investor.name",
#         read_only=True
#     )

#     business_name = serializers.CharField(
#         source="investment.business.name",
#         read_only=True
#     )

#     scheme_display_name = serializers.SerializerMethodField()

#     attachments = serializers.SerializerMethodField()

#     def get_attachments(self, obj):

#         qs = obj.attachments.filter(
#             deleted_at__isnull=True
#         )

#         return CertificateAttachmentSerializer(
#             qs,
#             many=True,
#             context=self.context
#         ).data

#     def get_scheme_display_name(self, obj):

#         scheme = obj.investment.scheme

#         return (
#             f"{scheme.get_category_display()}-"
#             f"{scheme.name}-"
#             f"{scheme.tenure_months} months-"
#             f"{scheme.annual_interest_rate}% p.a."
#         )

#     class Meta:
#         model = Certificate

#         fields = (
#             "id",
#             "investment",
#             "investor_name",
#             "business_name",
#             "scheme_display_name",
#             "html_content",
#             "version",
#             "attachments",
#             "created_by",
#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = (
#             "created_by",
#             "created_at",
#             "updated_at",
#             "version",
#         )


# # ============================================================
# # AUDIT LOG
# # ============================================================

# class AuditLogSerializer(
#     serializers.ModelSerializer
# ):

#     actor_name = serializers.CharField(
#         source="actor.username",
#         read_only=True
#     )

#     class Meta:
#         model = AuditLog

#         fields = (
#             "id",
#             "actor",
#             "actor_name",
#             "operation",
#             "module",
#             "object_type",
#             "object_id",
#             "description",
#             "metadata",
#             "created_at",
#         )


# # ============================================================
# # ACCOUNTANT PERMISSION
# # ============================================================

# class AccountantPermissionSerializer(
#     serializers.ModelSerializer
# ):

#     class Meta:
#         model = AccountantPermission

#         fields = (
#             "user",
#             "permissions",
#             "updated_at",
#         )

#         read_only_fields = (
#             "updated_at",
#         )


# # ============================================================
# # INVESTOR PORTAL PROFILE
# # ============================================================

# class InvestorProfileSerializer(
#     serializers.ModelSerializer
# ):

#     identity_details = serializers.SerializerMethodField()

#     bank_details = serializers.SerializerMethodField()

#     nominee_details = serializers.SerializerMethodField()

#     documents = InvestorDocumentSerializer(
#         source="uploaded_documents",
#         many=True,
#         read_only=True
#     )

#     total_investment = serializers.SerializerMethodField()

#     business_count = serializers.SerializerMethodField()

#     class Meta:
#         model = Investor

#         fields = (
#             "id",
#             "name",
#             "email",
#             "mobile",
#             "image",
#             "identity_details",
#             "bank_details",
#             "nominee_details",
#             "documents",
#             "other_details",
#             "total_investment",
#             "business_count",
#             "created_at",
#             "updated_at",
#         )

#     def _get_section(
#         self,
#         obj,
#         section
#     ):

#         return list(
#             obj.detail_fields
#             .filter(section=section)
#             .values(
#                 "id",
#                 "key",
#                 "value"
#             )
#         )

#     def get_identity_details(self, obj):

#         return self._get_section(
#             obj,
#             InvestorDetailField.Sections.IDENTITY
#         )

#     def get_bank_details(self, obj):

#         return self._get_section(
#             obj,
#             InvestorDetailField.Sections.BANK
#         )

#     def get_nominee_details(self, obj):

#         return self._get_section(
#             obj,
#             InvestorDetailField.Sections.NOMINEE
#         )

#     def get_total_investment(self, obj):

#         one_time = (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True,
#                 amount__isnull=False
#             )
#             .aggregate(
#                 total=Sum("amount")
#             )["total"]
#             or Decimal("0")
#         )

#         recurring = (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True,
#                 scheme__category=Scheme.Categories.PLATINUM,
#                 recurring_contributions__deleted_at__isnull=True,
#             )
#             .aggregate(
#                 total=Sum(
#                     "recurring_contributions__amount"
#                 )
#             )["total"]
#             or Decimal("0")
#         )

#         return one_time + recurring

#     def get_business_count(self, obj):

#         return (
#             obj.investments
#             .filter(
#                 deleted_at__isnull=True
#             )
#             .values("business")
#             .distinct()
#             .count()
#         )


# # ============================================================
# # INVESTOR PORTAL INVESTMENT
# # ============================================================

# class InvestorInvestmentSerializer(
#     serializers.ModelSerializer
# ):

#     business_name = serializers.CharField(
#         source="business.name",
#         read_only=True
#     )

#     scheme_name = serializers.CharField(
#         source="scheme.name",
#         read_only=True
#     )

#     scheme_display_name = serializers.SerializerMethodField()

#     scheme_category = serializers.SerializerMethodField()

#     withdrawal_type = serializers.SerializerMethodField()

#     scheme_interest = serializers.SerializerMethodField()

#     tenure_months = serializers.SerializerMethodField()

#     total_contributed = serializers.SerializerMethodField()

#     maturity_amount = serializers.SerializerMethodField()

#     premature_amount = serializers.SerializerMethodField()

#     interest_earned_till_date = serializers.SerializerMethodField()

#     terms_display = serializers.SerializerMethodField()
#     diamond_valuation_before_investment = serializers.SerializerMethodField()
#     diamond_valuation_after_investment = serializers.SerializerMethodField()
#     diamond_ownership_percentage = serializers.SerializerMethodField()
#     class Meta:
#         model = Investment

#         fields = (
#             "id",

#             "business",
#             "business_name",

#             "scheme",
#             "scheme_name",
#             "scheme_display_name",
#             "scheme_category",
#             "withdrawal_type",
#             "scheme_interest",
#             "tenure_months",

#             "amount",
#             "monthly_installment",
#             "investor_annual_rate",

#             "investment_date",
#             "maturity_date",
#             "status",

#             "total_contributed",
#             "maturity_amount",
#             "premature_amount",
#             "interest_earned_till_date",

#             "terms_snapshot",
#             "terms_display",
#             "diamond_valuation_before_investment",
#             "diamond_valuation_after_investment",
#             "diamond_ownership_percentage",
#             "created_at",
#             "updated_at",
#         )

#         read_only_fields = fields

#     def get_diamond_valuation_before_investment(self, obj):
#         return (obj.terms_snapshot or {}).get("diamond_valuation_before_investment")

#     def get_diamond_valuation_after_investment(self, obj):
#         return (obj.terms_snapshot or {}).get("diamond_valuation_after_investment")

#     def get_diamond_ownership_percentage(self, obj):
#         return (obj.terms_snapshot or {}).get("diamond_ownership_percentage")

#     def get_scheme_display_name(self, obj):

#         scheme = obj.scheme

#         return (
#             f"{scheme.get_category_display()}-"
#             f"{scheme.name}-"
#             f"{scheme.tenure_months} months-"
#             f"{scheme.annual_interest_rate}% p.a."
#         )

#     def get_scheme_category(self, obj):

#         return obj.scheme.get_category_display()

#     def get_withdrawal_type(self, obj):

#         if obj.scheme.category == Scheme.Categories.DIAMOND:
#             return "—"
#         return obj.scheme.withdrawal_type or "—"

#     def get_scheme_interest(self, obj):

#         return obj.scheme.annual_interest_rate

#     def get_tenure_months(self, obj):

#         return obj.scheme.tenure_months

#     def get_total_contributed(self, obj):

#         if (
#             obj.scheme.category
#             == Scheme.Categories.PLATINUM
#         ):

#             return (
#                 obj.recurring_contributions
#                 .filter(
#                     deleted_at__isnull=True
#                 )
#                 .aggregate(
#                     total=Sum("amount")
#                 )["total"]
#                 or Decimal("0")
#             )

#         return obj.amount or Decimal("0")

#     def get_maturity_amount(self, obj):

#         if obj.scheme.category == Scheme.Categories.DIAMOND:
#             # Investor panel must not expose a live/current return valuation
#             # for Diamond. Only the locked ownership percentage is shown.
#             return None

#         try:
#             return maturity_value(obj)

#         except (ValueError, TypeError):
#             return None

#     def get_premature_amount(self, obj):

#         try:
#             return premature_value(obj)

#         except (ValueError, TypeError):
#             return None

#     def get_interest_earned_till_date(self, obj):

#         try:
#             return interest_till_date(obj)

#         except (ValueError, TypeError):
#             return Decimal("0")

#     def get_terms_display(self, obj):

#         scheme = obj.scheme

#         if scheme.category == Scheme.Categories.DIAMOND:
#             ownership = (obj.terms_snapshot or {}).get("diamond_ownership_percentage")
#             return (
#                 f"Category - {scheme.get_category_display()}, "
#                 f"Tenure - {scheme.tenure_months} Months, "
#                 f"Interest Percentage - {scheme.annual_interest_rate}% p.a., "
#                 f"Maturity - Business Total Equity × {ownership or 'locked ownership'}%, "
#                 f"Premature Withdrawal - Not Available"
#             )

#         return (
#             f"Category - "
#             f"{scheme.get_category_display()}, "

#             f"Tenure - "
#             f"{scheme.tenure_months} Months, "

#             f"Interest Percentage - "
#             f"{scheme.annual_interest_rate}% p.a., "

#             f"Penalty - "
#             f"{scheme.penalty_rate}%"
#         )


import json
from decimal import Decimal

from django.contrib.auth.password_validation import validate_password
from django.db.models import Sum
from django.utils import timezone
from rest_framework import serializers

from .models import (
    User,
    Business,
    BusinessBankDetail,
    BusinessDetailField,
    BusinessDocument,
    Investor,
    InvestorDetailField,
    InvestorDocument,
    AccountantDetailField,
    AccountantDocument,
    Scheme,
    KuberBusinessInvestment,
    Investment,
    BalanceSheet,
    DailyFinancialStatement,
    LedgerEntry,
    Payout,
    Certificate,
    CertificateAttachment,
    AuditLog,
    RecurringContribution,
    AccountantPermission,
)

from agent_panel.models import Agent, AgentInvestmentShare, AgentLedgerEntry

from .services import (
    maturity_value,
    premature_value,
    interest_till_date,
)


# ============================================================
# USER SERIALIZERS
# ============================================================

class UserCreateSerializer(serializers.ModelSerializer):
    password = serializers.CharField(
        write_only=True,
        min_length=8
    )

    class Meta:
        model = User
        fields = (
            "id",
            "username",
            "email",
            "password",
            "first_name",
            "last_name",
            "phone",
            "image",
            "role",
        )

    def validate_password(self, value):
        validate_password(value)
        return value

    def create(self, validated_data):
        password = validated_data.pop("password")

        user = User(**validated_data)
        user.set_password(password)
        user.save()

        return user


class UserOutputSerializer(serializers.ModelSerializer):

    image = serializers.SerializerMethodField()
    login_password_display = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = (
            "id",
            "username",
            "email",
            "first_name",
            "last_name",
            "phone",
            "role",
            "image",
            "is_active",
            "is_staff",
            "is_superuser",
            "login_password_display",
        )


    def get_login_password_display(self, obj):
        return obj.admin_visible_password or "Not available for this existing account"

    def get_image(self, obj):
        request = self.context.get("request")
        image = None
        if obj.role == User.Roles.BUSINESS:
            business = getattr(obj, "business_profile", None)
            if business and business.business_photo:
                image = business.business_photo
        elif obj.role == User.Roles.INVESTOR:
            investor = getattr(obj, "investor_profile", None)
            if investor and investor.image:
                image = investor.image
        elif obj.role == User.Roles.ACCOUNTANT:
            image = obj.image
        else:
            image = obj.image
        if not image:
            return None
        try:
            return request.build_absolute_uri(image.url) if request else image.url
        except Exception:
            return None


# ============================================================
# KEY VALUE RELATION FIELD
# ============================================================

class KeyValueRelationField(serializers.Field):

    def __init__(self, relation_name, section=None, **kwargs):
        self.relation_name = relation_name
        self.section = section
        super().__init__(**kwargs)

    def get_attribute(self, instance):
        return instance

    def to_representation(self, instance):
        return [
            {
                "id": item.pk,
                "key": item.key,
                "value": item.value,
            }
            for item in getattr(
                instance,
                self.relation_name
            ).all()
            if (
                self.section is None
                or getattr(item, "section", None) == self.section
            )
        ]

    def to_internal_value(self, data):

        if isinstance(data, str):
            try:
                data = json.loads(data)
            except json.JSONDecodeError:
                raise serializers.ValidationError(
                    "Expected a JSON array of {key, value} objects."
                )

        if not isinstance(data, list):
            raise serializers.ValidationError(
                "Expected a list of {key, value} objects."
            )

        result = []

        for item in data:

            if (
                not isinstance(item, dict)
                or not str(item.get("key", "")).strip()
            ):
                raise serializers.ValidationError(
                    "Each item must contain a non-empty key and a value."
                )

            result.append(
                {
                    "key": str(item["key"]).strip(),
                    "value": str(item.get("value", "")),
                }
            )

        return result


# ============================================================
# BUSINESS DOCUMENT
# ============================================================

class BusinessDocumentSerializer(serializers.ModelSerializer):

    class Meta:
        model = BusinessDocument

        fields = (
            "id",
            "business",
            "title",
            "file",
            "uploaded_by",
            "created_at",
        )

        read_only_fields = (
            "uploaded_by",
            "created_at",
        )


class BusinessSerializer(serializers.ModelSerializer):

    login_username = serializers.CharField(
        write_only=True,
        required=False
    )

    login_password = serializers.CharField(
        write_only=True,
        required=False,
        min_length=8
    )

    login_username_display = serializers.SerializerMethodField()
    login_password_display = serializers.SerializerMethodField()

    # Business Email
    email = serializers.EmailField(
        write_only=True,
        required=True
    )

    business_email = serializers.SerializerMethodField(
        read_only=True
    )

    # Bank Details
    bank_details = KeyValueRelationField(
        "bank_detail_fields",
        required=False
    )

    # Business Details
    business_details = KeyValueRelationField(
        "detail_fields",
        required=False
    )

    documents = BusinessDocumentSerializer(
        source="uploaded_documents",
        many=True,
        read_only=True
    )

    investor_count = serializers.IntegerField(
        read_only=True
    )

    total_investment = serializers.SerializerMethodField()

    class Meta:
        model = Business

        fields = (
            "id",

            # Login
            "login_username",
            "login_password",
            "login_username_display",
            "login_password_display",

            # Business Email
            "email",
            "business_email",

            # Business
            "name",
            "location",
            "owner_name",
            "owner_aadhar",
            "owner_pan",
            "owner_mobile",
            "owner_sex",

            # Relations
            "bank_details",
            "business_details",
            "owner_photo",
            "business_photo",
            "documents",

            # Statistics
            "investor_count",
            "total_investment",

            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "created_at",
            "updated_at",
            "business_email",
        )

    def get_login_username_display(self, obj):
        return obj.user.username if obj.user else "—"

    def get_login_password_display(self, obj):
        return obj.user.admin_visible_password if obj.user and obj.user.admin_visible_password else "Not available for this existing account"

    def get_business_email(self, obj):
        if obj.user:
            return obj.user.email or ""
        return ""

    def get_total_investment(self, obj):

        one_time = (
            obj.investments
            .filter(
                deleted_at__isnull=True,
                amount__isnull=False
            )
            .aggregate(
                v=Sum("amount")
            )["v"]
            or Decimal("0")
        )

        recurring = (
            obj.investments
            .filter(
                deleted_at__isnull=True,
                scheme__category=Scheme.Categories.PLATINUM,
                recurring_contributions__deleted_at__isnull=True,
            )
            .aggregate(
                v=Sum("recurring_contributions__amount")
            )["v"]
            or Decimal("0")
        )

        return one_time + recurring

    def _replace_kv(
        self,
        business,
        bank_details=None,
        business_details=None
    ):

        if bank_details is not None:

            BusinessBankDetail.objects.filter(
                business=business
            ).delete()

            BusinessBankDetail.objects.bulk_create(
                [
                    BusinessBankDetail(
                        business=business,
                        key=x["key"],
                        value=x["value"],
                    )
                    for x in bank_details
                ]
            )

        if business_details is not None:

            BusinessDetailField.objects.filter(
                business=business
            ).delete()

            BusinessDetailField.objects.bulk_create(
                [
                    BusinessDetailField(
                        business=business,
                        key=x["key"],
                        value=x["value"],
                    )
                    for x in business_details
                ]
            )

    def create(self, validated_data):

        username = validated_data.pop(
            "login_username",
            None
        )

        password = validated_data.pop(
            "login_password",
            None
        )

        email = validated_data.pop(
            "email"
        )

        bank_details = validated_data.pop(
            "bank_details",
            []
        )

        business_details = validated_data.pop(
            "business_details",
            []
        )

        request = self.context.get("request")

        if not username:
            raise serializers.ValidationError({
                "login_username": "Login Username is required."
            })

        if not password:
            raise serializers.ValidationError({
                "login_password": "Login Password is required."
            })

        # Prevent duplicate email
        if User.objects.filter(
            email__iexact=email
        ).exists():
            raise serializers.ValidationError({
                "email": "This email is already registered."
            })

        # Prevent duplicate username
        if User.objects.filter(
            username__iexact=username
        ).exists():
            raise serializers.ValidationError({
                "login_username": "This username is already registered."
            })

        user = User.objects.create_user(
            username=username,
            email=email,
            password=password,
            role=User.Roles.BUSINESS,
            phone=validated_data.get(
                "owner_mobile",
                ""
            ),
            admin_visible_password=password,
        )

        validated_data["created_by"] = request.user
        validated_data["user"] = user

        business = Business.objects.create(
            **validated_data
        )

        self._replace_kv(
            business,
            bank_details,
            business_details
        )

        return business

    def update(self, instance, validated_data):

        validated_data.pop(
            "login_username",
            None
        )

        password = validated_data.pop(
            "login_password",
            None
        )

        email = validated_data.pop(
            "email",
            None
        )

        bank_details = validated_data.pop(
            "bank_details",
            None
        )

        business_details = validated_data.pop(
            "business_details",
            None
        )

        # Update Business Email
        if email is not None:

            existing_user = (
                User.objects
                .filter(
                    email__iexact=email
                )
                .exclude(
                    pk=instance.user_id
                )
                .first()
            )

            if existing_user:
                raise serializers.ValidationError({
                    "email": "This email is already registered."
                })

            if instance.user:
                instance.user.email = email
                instance.user.save(
                    update_fields=["email"]
                )

        # Update password if provided
        if password:

            if not instance.user:
                raise serializers.ValidationError({
                    "login_password":
                        "Business login account does not exist."
                })

            instance.user.set_password(password)
            instance.user.admin_visible_password = password

            instance.user.save(
                update_fields=["password", "admin_visible_password"]
            )

        instance = super().update(
            instance,
            validated_data
        )

        self._replace_kv(
            instance,
            bank_details,
            business_details
        )

        return instance
class AccountantDocumentSerializer(serializers.ModelSerializer):
    class Meta:
        model = AccountantDocument
        fields = ("id", "title", "file", "uploaded_by", "created_at")
        read_only_fields = ("uploaded_by", "created_at")


# ============================================================
# INVESTOR DOCUMENT
# ============================================================

class InvestorDocumentSerializer(serializers.ModelSerializer):

    class Meta:
        model = InvestorDocument

        fields = (
            "id",
            "section",
            "title",
            "file",
            "uploaded_by",
            "created_at",
        )

        read_only_fields = (
            "uploaded_by",
            "created_at",
        )


# ============================================================
# INVESTOR DETAIL FIELD
# ============================================================

class InvestorDetailFieldSerializer(serializers.ModelSerializer):

    class Meta:
        model = InvestorDetailField

        fields = (
            "id",
            "section",
            "key",
            "value",
        )


# ============================================================
# AGENT ATTRIBUTION FIELD
# ============================================================

class NullablePrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
    def to_internal_value(self, data):
        if data in ("", None):
            return None
        return super().to_internal_value(data)


# ============================================================
# INVESTOR
# ============================================================

class InvestorSerializer(serializers.ModelSerializer):

    login_username = serializers.CharField(
        write_only=True,
        required=False
    )

    login_username_display = serializers.SerializerMethodField()

    login_password_status = serializers.SerializerMethodField()

    login_password = serializers.CharField(
        write_only=True,
        required=False,
        min_length=8
    )

    login_password_display = serializers.SerializerMethodField()

    identity_details = KeyValueRelationField(
        "detail_fields",
        section="IDENTITY",
        required=False
    )

    bank_details = KeyValueRelationField(
        "detail_fields",
        section="BANK",
        required=False
    )

    nominee_details = KeyValueRelationField(
        "detail_fields",
        section="NOMINEE",
        required=False
    )

    identity_fields = serializers.SerializerMethodField()
    bank_fields = serializers.SerializerMethodField()
    nominee_fields = serializers.SerializerMethodField()

    documents = InvestorDocumentSerializer(
        source="uploaded_documents",
        many=True,
        read_only=True
    )

    total_investment = serializers.SerializerMethodField()

    agent = NullablePrimaryKeyRelatedField(
        queryset=Agent.objects.filter(deleted_at__isnull=True),
        required=False,
        allow_null=True,
    )
    agent_name = serializers.CharField(source="agent.name", read_only=True)

    business_count = serializers.IntegerField(
        read_only=True
    )

    class Meta:
        model = Investor

        fields = (
            "id",
            "login_username",
            "login_password",
            "login_username_display",
            "login_password_status",
            "login_password_display",
            "user",
            "name",
            "email",
            "mobile",
            "image",
            "identity_details",
            "bank_details",
            "nominee_details",
            "identity_fields",
            "bank_fields",
            "nominee_fields",
            "documents",
            "other_details",
            "agent",
            "agent_name",
            "total_investment",
            "business_count",
            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "created_by",
            "created_at",
            "updated_at",
        )

    def get_login_username_display(self, obj):
        return obj.user.username if obj.user else "—"

    def get_login_password_status(self, obj):

        return (
            "Password set (hidden for security)"
            if obj.user and obj.user.has_usable_password()
            else "No password set"
        )

    def get_login_password_display(self, obj):
        return obj.user.admin_visible_password if obj.user and obj.user.admin_visible_password else "Not available for this existing account"

    def get_total_investment(self, obj):

        one_time = (
            obj.investments
            .filter(
                deleted_at__isnull=True,
                amount__isnull=False
            )
            .aggregate(
                v=Sum("amount")
            )["v"]
            or Decimal("0")
        )

        recurring = (
            obj.investments
            .filter(
                deleted_at__isnull=True,
                scheme__category=Scheme.Categories.PLATINUM,
                recurring_contributions__deleted_at__isnull=True,
            )
            .aggregate(
                v=Sum(
                    "recurring_contributions__amount"
                )
            )["v"]
            or Decimal("0")
        )

        return one_time + recurring

    def _section(self, investor, section):

        return list(
            investor.detail_fields
            .filter(section=section)
            .values(
                "id",
                "key",
                "value"
            )
        )

    def get_identity_fields(self, obj):
        return self._section(
            obj,
            InvestorDetailField.Sections.IDENTITY
        )

    def get_bank_fields(self, obj):
        return self._section(
            obj,
            InvestorDetailField.Sections.BANK
        )

    def get_nominee_fields(self, obj):
        return self._section(
            obj,
            InvestorDetailField.Sections.NOMINEE
        )

    def _replace_section(
        self,
        investor,
        section,
        values
    ):

        InvestorDetailField.objects.filter(
            investor=investor,
            section=section
        ).delete()

        InvestorDetailField.objects.bulk_create(
            [
                InvestorDetailField(
                    investor=investor,
                    section=section,
                    key=x["key"],
                    value=x["value"],
                )
                for x in values
            ]
        )

    def _save_files(
        self,
        investor,
        request
    ):

        if not request:
            return

        mapping = {
            "identity_files":
                InvestorDocument.Sections.IDENTITY,

            "bank_files":
                InvestorDocument.Sections.BANK,

            "nominee_files":
                InvestorDocument.Sections.NOMINEE,
        }

        for input_name, section in mapping.items():

            titles = (
                request.data.getlist(
                    f"{input_name}_titles"
                )
                if hasattr(request.data, "getlist")
                else []
            )

            for index, file in enumerate(
                request.FILES.getlist(input_name)
            ):

                title = (
                    titles[index]
                    if index < len(titles)
                    else file.name
                )

                InvestorDocument.objects.create(
                    investor=investor,
                    section=section,
                    title=title or file.name,
                    file=file,
                    uploaded_by=request.user,
                )

    def create(self, validated_data):

        username = validated_data.pop(
            "login_username",
            None
        )

        password = validated_data.pop(
            "login_password",
            None
        )

        identity = validated_data.pop(
            "identity_details",
            []
        )

        bank = validated_data.pop(
            "bank_details",
            []
        )

        nominee = validated_data.pop(
            "nominee_details",
            []
        )

        request = self.context["request"]

        user = None

        if username and password:

            user = User.objects.create_user(
                username=username,
                password=password,
                email=(
                    validated_data.get("email")
                    or f"{username}@investor.local"
                ),
                role=User.Roles.INVESTOR,
                phone=validated_data.get(
                    "mobile",
                    ""
                ),
                admin_visible_password=password,
            )

        validated_data["created_by"] = request.user
        validated_data["user"] = user

        investor = super().create(
            validated_data
        )

        self._replace_section(
            investor,
            InvestorDetailField.Sections.IDENTITY,
            identity
        )

        self._replace_section(
            investor,
            InvestorDetailField.Sections.BANK,
            bank
        )

        self._replace_section(
            investor,
            InvestorDetailField.Sections.NOMINEE,
            nominee
        )

        self._save_files(
            investor,
            request
        )

        return investor

    def update(self, instance, validated_data):

        validated_data.pop(
            "login_username",
            None
        )

        password = validated_data.pop(
            "login_password",
            None
        )

        identity = validated_data.pop(
            "identity_details",
            None
        )

        bank = validated_data.pop(
            "bank_details",
            None
        )

        nominee = validated_data.pop(
            "nominee_details",
            None
        )

        request = self.context.get(
            "request"
        )

        instance = super().update(
            instance,
            validated_data
        )

        if password and instance.user:

            instance.user.set_password(
                password
            )
            instance.user.admin_visible_password = password

            instance.user.save(
                update_fields=["password", "admin_visible_password"]
            )

        if identity is not None:
            self._replace_section(
                instance,
                InvestorDetailField.Sections.IDENTITY,
                identity
            )

        if bank is not None:
            self._replace_section(
                instance,
                InvestorDetailField.Sections.BANK,
                bank
            )

        if nominee is not None:
            self._replace_section(
                instance,
                InvestorDetailField.Sections.NOMINEE,
                nominee
            )

        self._save_files(
            instance,
            request
        )

        return instance


# ============================================================
# SCHEME
# ============================================================

class SchemeSerializer(serializers.ModelSerializer):

    scheme_display_name = serializers.SerializerMethodField()

    class Meta:
        model = Scheme

        fields = (
            "id",
            "category",
            "name",
            "description",
            "tenure_months",
            "annual_interest_rate",
            "penalty_rate",
            "withdrawal_type",
            "compounding_frequency",
            "rules",
            "is_active",
            "scheme_display_name",
            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "created_by",
            "created_at",
            "updated_at",
            "scheme_display_name",
        )

    def get_scheme_display_name(self, obj):

        return (
            f"{obj.get_category_display()}-"
            f"{obj.name}-"
            f"{obj.tenure_months} months-"
            f"{obj.annual_interest_rate}% p.a."
        )

    # def validate(self, attrs):

    #     category = attrs.get(
    #         "category",
    #         getattr(
    #             self.instance,
    #             "category",
    #             None
    #         )
    #     )

    #     if (
    #         category == Scheme.Categories.SILVER
    #         and not attrs.get(
    #             "withdrawal_type",
    #             getattr(
    #                 self.instance,
    #                 "withdrawal_type",
    #                 ""
    #             )
    #         )
    #     ):
    #         raise serializers.ValidationError(
    #             {
    #                 "withdrawal_type":
    #                     "Required for Silver."
    #             }
    #         )

    #     if (
    #         category == Scheme.Categories.GOLD
    #         and not attrs.get(
    #             "compounding_frequency",
    #             getattr(
    #                 self.instance,
    #                 "compounding_frequency",
    #                 ""
    #             )
    #         )
    #     ):
    #         attrs["compounding_frequency"] = "MONTHLY"

    #     if category == Scheme.Categories.PLATINUM:
    #         # Platinum has no amount field on Scheme.
    #         pass

    #     if category == Scheme.Categories.DIAMOND:
    #         attrs["penalty_rate"] = Decimal("0")
    #         attrs["compounding_frequency"] = ""

    #     return attrs
    def validate(self, attrs):

        category = attrs.get(
            "category",
            getattr(self.instance, "category", None)
        )
    
        if not attrs.get(
            "withdrawal_type",
            getattr(self.instance, "withdrawal_type", "")
        ):
            attrs["withdrawal_type"] = "MONTHLY_WITHDRAWAL"
    
        if (
            category == Scheme.Categories.GOLD
            and not attrs.get(
                "compounding_frequency",
                getattr(self.instance, "compounding_frequency", "")
            )
        ):
            attrs["compounding_frequency"] = "MONTHLY"
    
        if category == Scheme.Categories.PLATINUM:
            pass
    
        if category == Scheme.Categories.DIAMOND:
            attrs["penalty_rate"] = Decimal("0")
            attrs["compounding_frequency"] = ""
    
        return attrs
    def to_representation(self, instance):
        data = super().to_representation(instance)
        if instance.category == Scheme.Categories.DIAMOND:
            data["penalty_rate"] = "0.0000"
            data["withdrawal_type"] = ""
            data["compounding_frequency"] = ""
        return data


# ============================================================
# KUBER → BUSINESS INVESTMENT
# ============================================================

from rest_framework import serializers


class KuberBusinessInvestmentSerializer(
    serializers.ModelSerializer
):
    business_name = serializers.CharField(
        source="business.name",
        read_only=True
    )

    interest = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )

    current_principal = serializers.SerializerMethodField()

    class Meta:
        model = KuberBusinessInvestment

        fields = (
            "id",
            "business",
            "business_name",
            "investment_type",
            "amount",
            "annual_rate",
            "equity_share_percentage",
            "current_principal",
            "investment_date",
            "maturity_date",
            "interest",
            "notes",
            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "current_principal",
            "interest",
            "created_at",
            "updated_at",
        )

    def get_current_principal(self, obj):
        try:
            from .services import kuber_equity_current_principal
            return kuber_equity_current_principal(obj)
        except (ValueError, TypeError):
            return None if obj.investment_type == KuberBusinessInvestment.InvestmentTypes.EQUITY_SHARE else obj.amount

    def validate(self, attrs):
        from .models import KuberBusinessInvestment

        investment_type = attrs.get(
            "investment_type",
            getattr(
                self.instance,
                "investment_type",
                KuberBusinessInvestment.InvestmentTypes.ORDINARY,
            ),
        )

        amount = attrs.get("amount", getattr(self.instance, "amount", None))
        if amount is None or Decimal(amount) <= 0:
            raise serializers.ValidationError({"amount": "Investment amount must be greater than zero."})

        investment_date = attrs.get("investment_date", getattr(self.instance, "investment_date", None))
        maturity_date = attrs.get("maturity_date", getattr(self.instance, "maturity_date", None))
        if self.instance is None and maturity_date is None:
            raise serializers.ValidationError({"maturity_date": "Maturity date is required for Kuber Business investment."})
        if maturity_date is not None and investment_date is not None and maturity_date < investment_date:
            raise serializers.ValidationError({"maturity_date": "Maturity date cannot be before the investment date."})

        if investment_type == KuberBusinessInvestment.InvestmentTypes.EQUITY_SHARE:
            attrs["annual_rate"] = Decimal("0")

            share = attrs.get(
                "equity_share_percentage",
                getattr(self.instance, "equity_share_percentage", None),
            )
            if share is None:
                raise serializers.ValidationError({
                    "equity_share_percentage": "Share percentage is required for Equity Share investment."
                })
            share = Decimal(share)
            if share <= 0 or share > 100:
                raise serializers.ValidationError({
                    "equity_share_percentage": "Share percentage must be greater than 0 and cannot exceed 100."
                })
            attrs["equity_share_percentage"] = share

            # Share percentage is stored only for display/reference. It is
            # deliberately not used in any financial calculation.

            # Keep the entered share percentage locked after creation.
            if self.instance is not None:
                immutable = {"business", "investment_type", "amount", "investment_date", "equity_share_percentage"}
                changed = {
                    field for field in immutable
                    if field in attrs and attrs[field] != getattr(self.instance, field)
                }
                if changed:
                    raise serializers.ValidationError({
                        field: "Equity Share investment terms are locked after creation."
                        for field in sorted(changed)
                    })
        else:
            attrs["equity_share_percentage"] = None
        return attrs

    def create(self, validated_data):
        from .services import create_kuber_business_investment
    
        created_by = validated_data.pop("created_by", None)
    
        return create_kuber_business_investment(
            business=validated_data["business"],
            investment_type=validated_data.get(
                "investment_type",
                KuberBusinessInvestment.InvestmentTypes.ORDINARY,
            ),
            amount=validated_data["amount"],
            annual_rate=validated_data.get("annual_rate", Decimal("0")),
            equity_share_percentage=validated_data.get("equity_share_percentage"),
            investment_date=validated_data.get("investment_date"),
            maturity_date=validated_data.get("maturity_date"),
            notes=validated_data.get("notes", ""),
            created_by=created_by,
        )


# ============================================================
# INVESTMENT
# ============================================================

class InvestmentSerializer(
    serializers.ModelSerializer
):

    investor_name = serializers.CharField(
        source="investor.name",
        read_only=True
    )

    business_name = serializers.CharField(
        source="business.name",
        read_only=True
    )

    scheme_name = serializers.CharField(
        source="scheme.name",
        read_only=True
    )

    scheme_display_name = serializers.SerializerMethodField()
    scheme_category = serializers.SerializerMethodField()
    withdrawal_type = serializers.SerializerMethodField()
    scheme_interest = serializers.SerializerMethodField()
    tenure_months = serializers.SerializerMethodField()
    maturity_amount = serializers.SerializerMethodField()
    premature_amount = serializers.SerializerMethodField()
    interest_earned_till_date = serializers.SerializerMethodField()
    total_contributed = serializers.SerializerMethodField()
    terms_display = serializers.SerializerMethodField()
    diamond_valuation_before_investment = serializers.SerializerMethodField()
    diamond_valuation_after_investment = serializers.SerializerMethodField()
    diamond_ownership_percentage = serializers.SerializerMethodField()
    diamond_maturity_valuation = serializers.SerializerMethodField()
    current_principal = serializers.SerializerMethodField()

    # Agent attribution is stored in AgentInvestmentShare because that model
    # already represents the per-investment commission percentage.
    agent = NullablePrimaryKeyRelatedField(
        queryset=Agent.objects.filter(deleted_at__isnull=True),
        required=False,
        allow_null=True,
        write_only=True,
    )
    agent_profit_percentage = serializers.DecimalField(
        max_digits=7,
        decimal_places=4,
        required=False,
        allow_null=True,
        write_only=True,
    )
    agent_name = serializers.SerializerMethodField()

    class Meta:
        model = Investment

        fields = (
            "id",
            "investor",
            "investor_name",
            "agent",
            "agent_name",
            "agent_profit_percentage",
            "business",
            "business_name",
            "scheme",
            "scheme_name",
            "scheme_display_name",
            "scheme_category",
            "withdrawal_type",
            "scheme_interest",
            "tenure_months",
            "amount",
            "monthly_installment",
            "investor_annual_rate",
            "investment_date",
            "maturity_date",
            "status",
            "terms_snapshot",
            "total_contributed",
            "maturity_amount",
            "premature_amount",
            "interest_earned_till_date",
            "terms_display",
            "diamond_valuation_before_investment",
            "diamond_valuation_after_investment",
            "diamond_ownership_percentage",
            "diamond_maturity_valuation",
            "current_principal",
            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "created_by",
            "maturity_date",
            "terms_snapshot",
            "investor_annual_rate",
            "created_at",
            "updated_at",
            "maturity_amount",
            "premature_amount",
            "total_contributed",
            "scheme_display_name",
            "interest_earned_till_date",
            "terms_display",
            "diamond_valuation_before_investment",
            "diamond_valuation_after_investment",
            "diamond_ownership_percentage",
            "diamond_maturity_valuation",
            "current_principal",
        )

    def get_agent_name(self, obj):
        share = getattr(obj, "agent_profit_share", None)
        return share.agent.name if share else None

    def to_representation(self, obj):
        data = super().to_representation(obj)
        share = getattr(obj, "agent_profit_share", None)
        data["agent"] = share.agent_id if share else None
        data["agent_name"] = share.agent.name if share else None
        data["agent_profit_percentage"] = share.profit_percentage if share else None
        return data

    def _save_agent_share(self, obj, agent, percentage, request):
        if agent is None:
            AgentInvestmentShare.objects.filter(investment=obj).delete()
            return
        if percentage is None:
            raise serializers.ValidationError({"agent_profit_percentage": "Agent percentage is required when an Agent is selected."})
        percentage = Decimal(str(percentage))
        if percentage < 0 or percentage > 100:
            raise serializers.ValidationError({"agent_profit_percentage": "Agent percentage must be between 0 and 100."})
        AgentInvestmentShare.objects.update_or_create(
            investment=obj,
            defaults={
                "agent": agent,
                "profit_percentage": percentage,
                "effective_from": obj.investment_date,
                "created_by": request.user,
            },
        )

    def get_diamond_valuation_before_investment(self, obj):
        return (obj.terms_snapshot or {}).get("diamond_valuation_before_investment")

    def get_diamond_valuation_after_investment(self, obj):
        return (obj.terms_snapshot or {}).get("diamond_valuation_after_investment")

    def get_diamond_ownership_percentage(self, obj):
        return (obj.terms_snapshot or {}).get("diamond_ownership_percentage")

    def get_diamond_maturity_valuation(self, obj):
        if obj.scheme.category != Scheme.Categories.DIAMOND:
            return None
        try:
            from .services import diamond_maturity_valuation
            return diamond_maturity_valuation(obj, as_of=obj.maturity_date)
        except (ValueError, TypeError):
            return None

    def get_current_principal(self, obj):
        try:
            from .services import investment_current_principal
            return investment_current_principal(obj)
        except (ValueError, TypeError):
            return None if obj.scheme.category == Scheme.Categories.DIAMOND else (obj.amount or Decimal("0"))

    def get_scheme_display_name(self, obj):

        return (
            f"{obj.scheme.get_category_display()}-"
            f"{obj.scheme.name}-"
            f"{obj.scheme.tenure_months} months-"
            f"{obj.scheme.annual_interest_rate}% p.a."
        )

    def get_scheme_category(self, obj):
        return obj.scheme.get_category_display()

    def get_withdrawal_type(self, obj):
        return obj.scheme.withdrawal_type or "—"

    def get_scheme_interest(self, obj):
        return obj.scheme.annual_interest_rate

    def get_tenure_months(self, obj):
        return obj.scheme.tenure_months

    def get_terms_display(self, obj):

        scheme = obj.scheme

        if scheme.category == Scheme.Categories.DIAMOND:
            ownership = (obj.terms_snapshot or {}).get("diamond_ownership_percentage")
            return (
                f"Category - {scheme.get_category_display()}, "
                f"Tenure - {scheme.tenure_months} Months, "
                f"Interest Percentage - {scheme.annual_interest_rate}% p.a., "
                f"Maturity - Business Total Equity × {ownership or 'locked ownership'}%, "
                f"Premature Withdrawal - Not Available"
            )

        return (
            f"Category - {scheme.get_category_display()}, "
            f"Tenure - {scheme.tenure_months} Months, "
            f"Interest Percentage - {scheme.annual_interest_rate}% p.a., "
            f"Penalty - {scheme.penalty_rate}%"
        )

    def get_interest_earned_till_date(self, obj):
        return interest_till_date(obj)

    def get_total_contributed(self, obj):

        if obj.scheme.category == Scheme.Categories.PLATINUM:

            return (
                obj.recurring_contributions
                .aggregate(
                    v=Sum("amount")
                )["v"]
                or Decimal("0")
            )

        return obj.amount or Decimal("0")

    def get_maturity_amount(self, obj):

        try:
            return maturity_value(obj)

        except ValueError:
            return None

    def get_premature_amount(self, obj):

        try:
            return premature_value(obj)

        except ValueError:
            return None

    def validate(self, attrs):

        scheme = attrs.get(
            "scheme",
            getattr(
                self.instance,
                "scheme",
                None
            )
        )

        amount = attrs.get(
            "amount",
            getattr(
                self.instance,
                "amount",
                None
            )
        )

        monthly = attrs.get(
            "monthly_installment",
            getattr(
                self.instance,
                "monthly_installment",
                None
            )
        )

        if scheme:

            if scheme.category == Scheme.Categories.PLATINUM:

                if monthly is None:

                    raise serializers.ValidationError(
                        {
                            "monthly_installment":
                                "Required for Platinum investment."
                        }
                    )

                attrs["amount"] = None

            else:

                if amount is None:

                    raise serializers.ValidationError(
                        {
                            "amount":
                                "Required for Silver, Gold and Diamond investment."
                        }
                    )

                attrs["monthly_installment"] = None

        return attrs

    def create(self, validated_data):
        from .services import create_investment_with_snapshot

        request = self.context["request"]
        agent = validated_data.pop("agent", None)
        percentage = validated_data.pop("agent_profit_percentage", None)
        scheme = validated_data["scheme"]

        obj = create_investment_with_snapshot(
            investor=validated_data["investor"],
            business=validated_data["business"],
            scheme=scheme,
            amount=validated_data.get("amount"),
            monthly_installment=validated_data.get("monthly_installment"),
            created_by=request.user,
            investment_date=validated_data.get("investment_date"),
        )
        self._save_agent_share(obj, agent, percentage, request)
        # Generate today's Agent soft-debit immediately when the investment
        # is already active today; the normal daily scheduler remains intact.
        if agent is not None and obj.investment_date <= timezone.localdate():
            try:
                from agent_panel.services import generate_daily_agent_ledger
                generate_daily_agent_ledger(timezone.localdate())
            except Exception:
                pass
        return obj

    def update(self, instance, validated_data):
        from .services import (
            months_after,
            build_terms_snapshot
        )

        request = self.context["request"]
        agent_supplied = "agent" in validated_data
        agent = validated_data.pop("agent", None) if agent_supplied else None
        percentage_supplied = "agent_profit_percentage" in validated_data
        percentage = validated_data.pop("agent_profit_percentage", None) if percentage_supplied else None

        scheme = validated_data.get(
            "scheme",
            instance.scheme
        )

        amount = validated_data.get(
            "amount",
            instance.amount
        )

        monthly = validated_data.get(
            "monthly_installment",
            instance.monthly_installment
        )

        if scheme.category == Scheme.Categories.PLATINUM:

            amount = None

        else:

            monthly = None

        instance = super().update(
            instance,
            {
                **validated_data,
                "amount": amount,
                "monthly_installment": monthly,
                "investor_annual_rate":
                    scheme.annual_interest_rate,
            }
        )

        instance.maturity_date = months_after(
            instance.investment_date,
            scheme.tenure_months
        )

        instance.terms_snapshot = build_terms_snapshot(
            scheme,
            instance
        )

        instance.save(
            update_fields=[
                "maturity_date",
                "terms_snapshot",
                "updated_at",
                "investor_annual_rate",
            ]
        )

        if agent_supplied:
            self._save_agent_share(instance, agent, percentage, request)
        elif percentage_supplied:
            share = getattr(instance, "agent_profit_share", None)
            if not share:
                raise serializers.ValidationError({"agent_profit_percentage": "Select an Agent before setting the percentage."})
            self._save_agent_share(instance, share.agent, percentage, request)

        return instance


# ============================================================
# BALANCE SHEET
# ============================================================

class BalanceSheetSerializer(serializers.ModelSerializer):
    business_name = serializers.CharField(
        source="business.name",
        read_only=True
    )

    # Show the username instead of the User ID
    entered_by = serializers.CharField(
        source="entered_by.username",
        read_only=True
    )

    total_current_assets = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )
    total_non_current_assets = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )
    total_assets = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )

    total_current_liabilities = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )
    total_non_current_liabilities = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )
    total_liabilities = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )

    total_equity = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )
    liabilities_and_equity = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )
    balance_difference = serializers.DecimalField(
        max_digits=14,
        decimal_places=2,
        read_only=True
    )

    is_balanced = serializers.BooleanField(read_only=True)

    class Meta:
        model = BalanceSheet

        fields = (
            "id",
            "business",
            "business_name",
            "statement_date",

            # Current Assets
            "cash_in_hand",
            "bank_balance",
            "accounts_receivable",
            "inventory",
            "prepaid_expenses",
            "other_current_assets",

            # Non-Current Assets
            "fixed_assets",
            "accumulated_depreciation",
            "other_non_current_assets",

            # Current Liabilities
            "accounts_payable",
            "short_term_borrowings",
            "accrued_expenses",
            "taxes_payable",
            "other_current_liabilities",

            # Non-Current Liabilities
            "long_term_loans",
            "other_non_current_liabilities",

            # Equity
            "owner_capital",
            "reserves_surplus",
            "retained_earnings",
            "drawings",

            "remarks",

            # Calculated fields
            "total_current_assets",
            "total_non_current_assets",
            "total_assets",
            "total_current_liabilities",
            "total_non_current_liabilities",
            "total_liabilities",
            "total_equity",
            "liabilities_and_equity",
            "balance_difference",
            "is_balanced",

            # User who entered the balance sheet
            "entered_by",

            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "entered_by",
            "created_at",
            "updated_at",
        )

    def validate(self, attrs):
        # Decimal fields reject invalid/negative values at model level.
        # Totals are calculated by the backend.
        # An unbalanced statement can still be saved and is explicitly flagged.
        return attrs


# ============================================================
# DAILY FINANCIAL STATEMENT / P&L
# ============================================================

class DailyFinancialStatementSerializer(serializers.ModelSerializer):
    business_name = serializers.CharField(source="business.name", read_only=True)
    submitted_by = serializers.CharField(source="submitted_by.username", read_only=True)
    reviewed_by = serializers.CharField(source="reviewed_by.username", read_only=True, allow_null=True)

    gross_profit = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    operating_profit = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    profit_before_tax = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    net_profit = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    profit_margin = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    financial_status = serializers.CharField(read_only=True)
    operating_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    investing_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    financing_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)
    net_cash_flow = serializers.DecimalField(max_digits=14, decimal_places=2, read_only=True)

    class Meta:
        model = DailyFinancialStatement
        fields = (
            "id", "business", "business_name", "statement_date",
            "revenue", "direct_cost", "operating_expenses", "other_income",
            "finance_cost", "tax", "gross_profit", "operating_profit",
            "profit_before_tax", "net_profit", "profit_margin", "financial_status",
            "operating_cash_inflow", "operating_cash_outflow",
            "investing_cash_inflow", "investing_cash_outflow",
            "financing_cash_inflow", "financing_cash_outflow",
            "operating_cash_flow", "investing_cash_flow", "financing_cash_flow",
            "net_cash_flow", "remarks", "status", "submitted_by", "submitted_at",
            "reviewed_by", "reviewed_at", "created_at", "updated_at",
        )
        read_only_fields = (
            "submitted_by", "submitted_at", "reviewed_by", "reviewed_at",
            "created_at", "updated_at", "gross_profit", "operating_profit",
            "profit_before_tax", "net_profit", "profit_margin", "financial_status",
            "operating_cash_flow", "investing_cash_flow", "financing_cash_flow",
            "net_cash_flow",
        )

    def validate(self, attrs):
        for field in (
            "revenue", "direct_cost", "operating_expenses", "other_income",
            "finance_cost", "tax", "operating_cash_inflow", "operating_cash_outflow",
            "investing_cash_inflow", "investing_cash_outflow",
            "financing_cash_inflow", "financing_cash_outflow",
        ):
            value = attrs.get(field)
            if value is not None and value < 0:
                raise serializers.ValidationError({field: "This value cannot be negative."})
        return attrs


class InvestorBusinessPerformanceSerializer(serializers.Serializer):
    """Investor-safe view; never exposes assets, liabilities, equity or cash balances."""
    business_id = serializers.IntegerField()
    business_name = serializers.CharField()
    period = serializers.DateField(source="statement_date")
    status = serializers.CharField(source="financial_status")
    net_profit = serializers.DecimalField(max_digits=14, decimal_places=2)
    profit_margin = serializers.DecimalField(max_digits=14, decimal_places=2)
    updated_at = serializers.DateTimeField()
    change_from_previous_period = serializers.DecimalField(
        max_digits=14, decimal_places=2, allow_null=True
    )


# ============================================================
# LEDGER
# ============================================================

class LedgerEntrySerializer(serializers.ModelSerializer):

    investor_name = serializers.CharField(
        source="investor.name",
        read_only=True
    )

    business_name = serializers.CharField(
        source="business.name",
        read_only=True
    )

    scheme = serializers.SerializerMethodField()

    class Meta:
        model = LedgerEntry

        fields = (
            "id",
            "business",
            "business_name",
            "investment",
            "investor",
            "investor_name",
            "scheme",
            "kuber_investment",
            "entry_date",
            "entry_type",
            "amount",
            "annual_rate",
            "description",
            "reference",
            "created_at",
        )

        read_only_fields = (
            "created_at",
            "scheme",
        )

    def get_scheme(self, obj):
        if not obj.investment or not obj.investment.scheme:
            return None

        scheme = obj.investment.scheme

        return {
            "category": scheme.get_category_display(),
            "name": scheme.name,
            "tenure_months": scheme.tenure_months,
            "annual_interest_rate": scheme.annual_interest_rate,
            "display": (
                f"{scheme.get_category_display()} - "
                f"{scheme.name} - "
                f"{scheme.tenure_months} Months - "
                f"{scheme.annual_interest_rate}% p.a."
            ),
        }


# ============================================================
# PAYOUT
# ============================================================

class PayoutSerializer(
    serializers.ModelSerializer
):

    investor_name = serializers.CharField(
        source="investor.name",
        read_only=True
    )

    class Meta:
        model = Payout

        fields = (
            "id",
            "investment",
            "investor",
            "investor_name",
            "amount",
            "payout_date",
            "notes",
            "created_by",
            "created_at",
        )

        read_only_fields = (
            "investor",
            "created_by",
            "created_at",
        )

    def validate(self, attrs):

        investment = attrs["investment"]

        if investment.status in (
            Investment.Status.CANCELLED,
            Investment.Status.CLOSED,
        ):

            raise serializers.ValidationError(
                "Cannot create payout for a closed/cancelled investment."
            )

        attrs["investor"] = investment.investor

        return attrs


# ============================================================
# RECURRING CONTRIBUTION
# ============================================================

class RecurringContributionSerializer(
    serializers.ModelSerializer
):

    class Meta:
        model = RecurringContribution

        fields = (
            "id",
            "investment",
            "contribution_date",
            "amount",
            "created_by",
            "created_at",
        )

        read_only_fields = (
            "created_by",
            "created_at",
        )

    def validate(self, attrs):

        investment = self.context.get(
            "investment"
        )

        if (
            investment
            and investment.scheme.category
            != Scheme.Categories.PLATINUM
        ):

            raise serializers.ValidationError(
                "Recurring contributions are only for Platinum investments."
            )

        return attrs


# ============================================================
# CERTIFICATE ATTACHMENT
# ============================================================

class CertificateAttachmentSerializer(
    serializers.ModelSerializer
):

    class Meta:
        model = CertificateAttachment

        fields = (
            "id",
            "title",
            "file",
            "sort_order",
            "created_at",
        )

        read_only_fields = (
            "created_at",
        )


# ============================================================
# CERTIFICATE
# ============================================================

class CertificateSerializer(
    serializers.ModelSerializer
):

    investor_name = serializers.CharField(
        source="investment.investor.name",
        read_only=True
    )

    business_name = serializers.CharField(
        source="investment.business.name",
        read_only=True
    )

    scheme_display_name = serializers.SerializerMethodField()

    attachments = serializers.SerializerMethodField()

    def get_attachments(self, obj):

        qs = obj.attachments.filter(
            deleted_at__isnull=True
        )

        return CertificateAttachmentSerializer(
            qs,
            many=True,
            context=self.context
        ).data

    def get_scheme_display_name(self, obj):

        scheme = obj.investment.scheme

        return (
            f"{scheme.get_category_display()}-"
            f"{scheme.name}-"
            f"{scheme.tenure_months} months-"
            f"{scheme.annual_interest_rate}% p.a."
        )

    class Meta:
        model = Certificate

        fields = (
            "id",
            "investment",
            "investor_name",
            "business_name",
            "scheme_display_name",
            "html_content",
            "version",
            "attachments",
            "created_by",
            "created_at",
            "updated_at",
        )

        read_only_fields = (
            "created_by",
            "created_at",
            "updated_at",
            "version",
        )


# ============================================================
# AUDIT LOG
# ============================================================

class AuditLogSerializer(
    serializers.ModelSerializer
):

    actor_name = serializers.CharField(
        source="actor.username",
        read_only=True
    )

    class Meta:
        model = AuditLog

        fields = (
            "id",
            "actor",
            "actor_name",
            "operation",
            "module",
            "object_type",
            "object_id",
            "description",
            "metadata",
            "created_at",
        )


# ============================================================
# ACCOUNTANT PERMISSION
# ============================================================

class AccountantPermissionSerializer(
    serializers.ModelSerializer
):

    class Meta:
        model = AccountantPermission

        fields = (
            "user",
            "permissions",
            "updated_at",
        )

        read_only_fields = (
            "updated_at",
        )


# ============================================================
# INVESTOR PORTAL PROFILE
# ============================================================

class InvestorProfileSerializer(
    serializers.ModelSerializer
):

    identity_details = serializers.SerializerMethodField()

    bank_details = serializers.SerializerMethodField()

    nominee_details = serializers.SerializerMethodField()

    documents = InvestorDocumentSerializer(
        source="uploaded_documents",
        many=True,
        read_only=True
    )

    total_investment = serializers.SerializerMethodField()

    business_count = serializers.SerializerMethodField()

    class Meta:
        model = Investor

        fields = (
            "id",
            "name",
            "email",
            "mobile",
            "image",
            "identity_details",
            "bank_details",
            "nominee_details",
            "documents",
            "other_details",
            "total_investment",
            "business_count",
            "created_at",
            "updated_at",
        )

    def _get_section(
        self,
        obj,
        section
    ):

        return list(
            obj.detail_fields
            .filter(section=section)
            .values(
                "id",
                "key",
                "value"
            )
        )

    def get_identity_details(self, obj):

        return self._get_section(
            obj,
            InvestorDetailField.Sections.IDENTITY
        )

    def get_bank_details(self, obj):

        return self._get_section(
            obj,
            InvestorDetailField.Sections.BANK
        )

    def get_nominee_details(self, obj):

        return self._get_section(
            obj,
            InvestorDetailField.Sections.NOMINEE
        )

    def get_total_investment(self, obj):

        one_time = (
            obj.investments
            .filter(
                deleted_at__isnull=True,
                amount__isnull=False
            )
            .aggregate(
                total=Sum("amount")
            )["total"]
            or Decimal("0")
        )

        recurring = (
            obj.investments
            .filter(
                deleted_at__isnull=True,
                scheme__category=Scheme.Categories.PLATINUM,
                recurring_contributions__deleted_at__isnull=True,
            )
            .aggregate(
                total=Sum(
                    "recurring_contributions__amount"
                )
            )["total"]
            or Decimal("0")
        )

        return one_time + recurring

    def get_business_count(self, obj):

        return (
            obj.investments
            .filter(
                deleted_at__isnull=True
            )
            .values("business")
            .distinct()
            .count()
        )


# ============================================================
# INVESTOR PORTAL INVESTMENT
# ============================================================

class InvestorInvestmentSerializer(
    serializers.ModelSerializer
):

    business_name = serializers.CharField(
        source="business.name",
        read_only=True
    )

    scheme_name = serializers.CharField(
        source="scheme.name",
        read_only=True
    )

    scheme_display_name = serializers.SerializerMethodField()

    scheme_category = serializers.SerializerMethodField()

    withdrawal_type = serializers.SerializerMethodField()

    scheme_interest = serializers.SerializerMethodField()

    tenure_months = serializers.SerializerMethodField()

    total_contributed = serializers.SerializerMethodField()

    maturity_amount = serializers.SerializerMethodField()

    premature_amount = serializers.SerializerMethodField()

    interest_earned_till_date = serializers.SerializerMethodField()

    terms_display = serializers.SerializerMethodField()
    diamond_valuation_before_investment = serializers.SerializerMethodField()
    diamond_valuation_after_investment = serializers.SerializerMethodField()
    diamond_ownership_percentage = serializers.SerializerMethodField()
    class Meta:
        model = Investment

        fields = (
            "id",

            "business",
            "business_name",

            "scheme",
            "scheme_name",
            "scheme_display_name",
            "scheme_category",
            "withdrawal_type",
            "scheme_interest",
            "tenure_months",

            "amount",
            "monthly_installment",
            "investor_annual_rate",

            "investment_date",
            "maturity_date",
            "status",

            "total_contributed",
            "maturity_amount",
            "premature_amount",
            "interest_earned_till_date",

            "terms_snapshot",
            "terms_display",
            "diamond_valuation_before_investment",
            "diamond_valuation_after_investment",
            "diamond_ownership_percentage",
            "created_at",
            "updated_at",
        )

        read_only_fields = fields

    def get_diamond_valuation_before_investment(self, obj):
        return (obj.terms_snapshot or {}).get("diamond_valuation_before_investment")

    def get_diamond_valuation_after_investment(self, obj):
        return (obj.terms_snapshot or {}).get("diamond_valuation_after_investment")

    def get_diamond_ownership_percentage(self, obj):
        return (obj.terms_snapshot or {}).get("diamond_ownership_percentage")

    def get_scheme_display_name(self, obj):

        scheme = obj.scheme

        return (
            f"{scheme.get_category_display()}-"
            f"{scheme.name}-"
            f"{scheme.tenure_months} months-"
            f"{scheme.annual_interest_rate}% p.a."
        )

    def get_scheme_category(self, obj):

        return obj.scheme.get_category_display()

    def get_withdrawal_type(self, obj):

        if obj.scheme.category == Scheme.Categories.DIAMOND:
            return "—"
        return obj.scheme.withdrawal_type or "—"

    def get_scheme_interest(self, obj):

        return obj.scheme.annual_interest_rate

    def get_tenure_months(self, obj):

        return obj.scheme.tenure_months

    def get_total_contributed(self, obj):

        if (
            obj.scheme.category
            == Scheme.Categories.PLATINUM
        ):

            return (
                obj.recurring_contributions
                .filter(
                    deleted_at__isnull=True
                )
                .aggregate(
                    total=Sum("amount")
                )["total"]
                or Decimal("0")
            )

        return obj.amount or Decimal("0")

    def get_maturity_amount(self, obj):

        if obj.scheme.category == Scheme.Categories.DIAMOND:
            # Investor panel must not expose a live/current return valuation
            # for Diamond. Only the locked ownership percentage is shown.
            return None

        try:
            return maturity_value(obj)

        except (ValueError, TypeError):
            return None

    def get_premature_amount(self, obj):

        try:
            return premature_value(obj)

        except (ValueError, TypeError):
            return None

    def get_interest_earned_till_date(self, obj):

        try:
            return interest_till_date(obj)

        except (ValueError, TypeError):
            return Decimal("0")

    def get_terms_display(self, obj):

        scheme = obj.scheme

        if scheme.category == Scheme.Categories.DIAMOND:
            ownership = (obj.terms_snapshot or {}).get("diamond_ownership_percentage")
            return (
                f"Category - {scheme.get_category_display()}, "
                f"Tenure - {scheme.tenure_months} Months, "
                f"Interest Percentage - {scheme.annual_interest_rate}% p.a., "
                f"Maturity - Business Total Equity × {ownership or 'locked ownership'}%, "
                f"Premature Withdrawal - Not Available"
            )

        return (
            f"Category - "
            f"{scheme.get_category_display()}, "

            f"Tenure - "
            f"{scheme.tenure_months} Months, "

            f"Interest Percentage - "
            f"{scheme.annual_interest_rate}% p.a., "

            f"Penalty - "
            f"{scheme.penalty_rate}%"
        )
