from drf_spectacular.utils import extend_schema_field
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.db import transaction
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.contenttypes.models import ContentType
from checkpoint.models import CheckPoint, FacilityType

from .models import (
    SubmissionType,
    CommodityCategory,
    CommodityType,
    UnitOfMeasurement,
    UnitConversion,
    SubmissionStatus,
    CollectionCategory,
    CollectionType,
    MetadataDeletionRequest,
    Submission,
    ImportSubmission,
    ExportSubmission,
    RevenueFineSubmission,
    HealthInspectionSubmission,
    DestructionSubmission,
    Document,
    SubmissionStatusHistory,
)

User = get_user_model()

# ==========================================
# Supporting Model Serializers
# ==========================================


class UserSummarySerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("id", "email", "name", "role", "status")


class RecordFacilityTypeSummarySerializer(serializers.ModelSerializer):
    class Meta:
        model = FacilityType
        fields = ("id", "name", "description", "created_at", "updated_at")


class RecordCheckPointSummarySerializer(serializers.ModelSerializer):
    facility = RecordFacilityTypeSummarySerializer(
        source="facility_type", read_only=True
    )

    class Meta:
        model = CheckPoint
        fields = (
            "id",
            "name",
            "location",
            "is_active",
            "facility",
            "created_at",
            "updated_at",
        )


class SubmissionTypeSerializer(serializers.ModelSerializer):
    class Meta:
        model = SubmissionType
        fields = "__all__"


class CommodityCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = CommodityCategory
        fields = "__all__"


class CommodityTypeSerializer(serializers.ModelSerializer):
    category_detail = CommodityCategorySerializer(source="category", read_only=True)

    class Meta:
        model = CommodityType
        fields = ("id", "category", "category_detail", "name", "description")


class UnitOfMeasurementSerializer(serializers.ModelSerializer):
    class Meta:
        model = UnitOfMeasurement
        fields = "__all__"


class UnitConversionSerializer(serializers.ModelSerializer):
    from_unit_detail = UnitOfMeasurementSerializer(source="from_unit", read_only=True)
    to_unit_detail = UnitOfMeasurementSerializer(source="to_unit", read_only=True)

    class Meta:
        model = UnitConversion
        fields = (
            "id",
            "from_unit",
            "from_unit_detail",
            "to_unit",
            "to_unit_detail",
            "factor",
            "created_at",
            "updated_at",
        )

    def validate(self, attrs):
        attrs = super().validate(attrs)
        from_unit = attrs.get("from_unit", getattr(self.instance, "from_unit", None))
        to_unit = attrs.get("to_unit", getattr(self.instance, "to_unit", None))
        factor = attrs.get("factor", getattr(self.instance, "factor", None))

        if from_unit and to_unit:
            if from_unit == to_unit:
                raise serializers.ValidationError(
                    {"to_unit": "From unit and to unit must be different."}
                )

            if not from_unit.dimension or not to_unit.dimension:
                raise serializers.ValidationError(
                    {
                        "from_unit": "Both units must have a dimension before creating a conversion.",
                        "to_unit": "Both units must have a dimension before creating a conversion.",
                    }
                )

            if from_unit.dimension != to_unit.dimension:
                raise serializers.ValidationError(
                    {"to_unit": "Units must belong to the same dimension."}
                )

        if factor is not None and factor <= 0:
            raise serializers.ValidationError(
                {"factor": "Conversion factor must be greater than zero."}
            )

        return attrs


class SubmissionStatusSerializer(serializers.ModelSerializer):
    class Meta:
        model = SubmissionStatus
        fields = "__all__"


class CollectionCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = CollectionCategory
        fields = "__all__"


class CollectionTypeSerializer(serializers.ModelSerializer):
    category_detail = CollectionCategorySerializer(source="category", read_only=True)

    class Meta:
        model = CollectionType
        fields = ("id", "category", "category_detail", "name", "description")


class MetadataDeletionRequestSerializer(serializers.ModelSerializer):
    requested_by_detail = UserSummarySerializer(source="requested_by", read_only=True)
    reviewed_by_detail = UserSummarySerializer(source="reviewed_by", read_only=True)
    target_exists = serializers.SerializerMethodField(read_only=True)
    target_display_name = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = MetadataDeletionRequest
        fields = (
            "id",
            "target_type",
            "target_id",
            "target_name",
            "target_display_name",
            "target_exists",
            "reason",
            "status",
            "requested_by",
            "requested_by_detail",
            "reviewed_by",
            "reviewed_by_detail",
            "reviewed_at",
            "review_note",
            "created_at",
            "updated_at",
        )
        read_only_fields = (
            "status",
            "requested_by",
            "requested_by_detail",
            "reviewed_by",
            "reviewed_by_detail",
            "reviewed_at",
            "target_name",
            "target_display_name",
            "target_exists",
            "created_at",
            "updated_at",
        )

    def validate(self, attrs):
        attrs = super().validate(attrs)
        target_type = attrs.get("target_type", getattr(self.instance, "target_type", None))
        target_id = attrs.get("target_id", getattr(self.instance, "target_id", None))

        if not target_type:
            raise serializers.ValidationError({"target_type": "This field is required."})
        if not target_id:
            raise serializers.ValidationError({"target_id": "This field is required."})

        target_model = MetadataDeletionRequest.target_model_map().get(target_type)
        if target_model is None:
            raise serializers.ValidationError({"target_type": "Unsupported metadata target."})

        target_obj = target_model.objects.filter(pk=target_id).first()
        if target_obj is None:
            raise serializers.ValidationError(
                {"target_id": "Target item not found for the selected target type."}
            )

        pending_qs = MetadataDeletionRequest.objects.filter(
            target_type=target_type,
            target_id=target_id,
            status=MetadataDeletionRequest.StatusChoices.PENDING,
        )
        if self.instance:
            pending_qs = pending_qs.exclude(pk=self.instance.pk)
        if pending_qs.exists():
            raise serializers.ValidationError(
                {"target_id": "A pending delete request already exists for this item."}
            )

        attrs["_target_obj"] = target_obj
        return attrs

    def create(self, validated_data):
        target_obj = validated_data.pop("_target_obj", None)
        if target_obj is not None:
            validated_data["target_name"] = str(target_obj)
        return super().create(validated_data)

    @extend_schema_field(serializers.BooleanField())
    def get_target_exists(self, obj):
        return obj.resolve_target() is not None

    @extend_schema_field(serializers.CharField(allow_blank=True))
    def get_target_display_name(self, obj):
        target_obj = obj.resolve_target()
        if target_obj is not None:
            return str(target_obj)
        return obj.target_name


class MetadataDeletionRequestDecisionSerializer(serializers.Serializer):
    review_note = serializers.CharField(required=False, allow_blank=True, allow_null=True)
    confirm_deletion = serializers.BooleanField(required=False, default=False)


class DocumentSerializer(serializers.ModelSerializer):
    uploaded_by_detail = UserSummarySerializer(source="uploaded_by", read_only=True)
    file_url = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Document
        fields = (
            "id",
            "submission",
            "uploaded_by",
            "uploaded_by_detail",
            "file",
            "file_url",
            "is_verified",
            "remarks",
            "created_at",
        )
        read_only_fields = ("uploaded_by", "is_verified")

    @extend_schema_field(serializers.URLField(allow_null=True))
    def get_file_url(self, obj):
        if not obj.file:
            return None

        # Backward-safe fallback for legacy DB values that may still hold full URLs.
        raw_value = str(obj.file)
        if raw_value.startswith(("http://", "https://")):
            return raw_value

        request = self.context.get("request")
        file_url = obj.file.url
        return request.build_absolute_uri(file_url) if request else file_url


class SubmissionStatusHistorySerializer(serializers.ModelSerializer):
    updated_by_detail = UserSummarySerializer(source="updated_by", read_only=True)
    from_status_detail = SubmissionStatusSerializer(
        source="from_status", read_only=True
    )
    to_status_detail = SubmissionStatusSerializer(source="to_status", read_only=True)

    class Meta:
        model = SubmissionStatusHistory
        fields = (
            "id",
            "submission",
            "updated_by",
            "updated_by_detail",
            "from_status",
            "from_status_detail",
            "to_status",
            "to_status_detail",
            "remarks",
            "created_at",
        )


class SubmissionStatusChangeSerializer(serializers.Serializer):
    status_id = serializers.UUIDField(
        help_text="The internal UUID of the new workflow state."
    )
    remarks = serializers.CharField(
        required=False, 
        allow_blank=True,
        help_text="Optional comments by the administrator explaining why the workflow state was updated."
    )

    def validate_status_id(self, value):
        from .models import SubmissionStatus
        status_obj = SubmissionStatus.objects.filter(id=value).first()
        if not status_obj:
            raise serializers.ValidationError("A valid 'SubmissionStatus' with this exact UUID does not exist.")
        return status_obj


# ==========================================
# Base Submission Serializers
# ==========================================


class SubmissionBaseSerializer(serializers.ModelSerializer):
    """Base fields for read serializers"""

    submission_type_detail = SubmissionTypeSerializer(
        source="submission_type", read_only=True
    )
    checkpoint_detail = RecordCheckPointSummarySerializer(
        source="checkpoint", read_only=True
    )
    status_detail = SubmissionStatusSerializer(source="status", read_only=True)
    created_by_detail = UserSummarySerializer(source="created_by", read_only=True)
    updated_by_detail = UserSummarySerializer(source="updated_by", read_only=True)

    # Nested documents and history (optional, can be included in detailed views)
    documents = DocumentSerializer(many=True, read_only=True)
    status_history = SubmissionStatusHistorySerializer(many=True, read_only=True)

    class Meta:
        model = Submission
        fields = (
            "id",
            "tracking_code",
            "refrence_no",
            "submission_type",
            "submission_type_detail",
            "checkpoint",
            "checkpoint_detail",
            "status",
            "status_detail",
            "submitted_at",
            "remarks",
            "created_by",
            "created_by_detail",
            "updated_by",
            "updated_by_detail",
            "created_at",
            "updated_at",
            "documents",
            "status_history",
        )


class SubmissionWriteBaseSerializer(serializers.ModelSerializer):
    checkpoint = serializers.PrimaryKeyRelatedField(
        read_only=True,
        help_text=(
            "Server-managed field. On create, checkpoint is taken from the "
            "authenticated user's assigned checkpoint."
        ),
    )
    documents = serializers.ListField(
        child=serializers.FileField(allow_empty_file=False, use_url=False),
        write_only=True,
        required=False,
        allow_empty=True,
        allow_null=True,
        help_text=(
            "Optional document files uploaded with this submission. Use "
            "multipart/form-data and repeat the 'documents' field to upload "
            "multiple files."
        ),
    )
    document_remarks = serializers.CharField(
        write_only=True,
        required=False,
        allow_blank=True,
        help_text="Optional remarks applied to every uploaded document in this request.",
    )

    class Meta:
        extra_kwargs = {"status": {"required": False}}

    def _create_submission_documents(self, submission, documents, remarks):
        if not documents:
            return

        request = self.context.get("request")
        user = getattr(request, "user", None)
        if not user or not user.is_authenticated:
            raise serializers.ValidationError(
                {"documents": "Authenticated user is required to upload documents."}
            )

        normalized_remarks = remarks or None
        for uploaded_file in documents:
            Document.objects.create(
                submission=submission,
                uploaded_by=user,
                file=uploaded_file,
                remarks=normalized_remarks,
            )

    @transaction.atomic
    def create(self, validated_data):
        documents = validated_data.pop("documents", [])
        document_remarks = validated_data.pop("document_remarks", "")
        instance = super().create(validated_data)
        self._create_submission_documents(
            submission=instance,
            documents=documents,
            remarks=document_remarks,
        )
        return instance

    @transaction.atomic
    def update(self, instance, validated_data):
        documents = validated_data.pop("documents", [])
        document_remarks = validated_data.pop("document_remarks", "")
        instance = super().update(instance, validated_data)
        self._create_submission_documents(
            submission=instance,
            documents=documents,
            remarks=document_remarks,
        )
        return instance


class TargetSubmissionWriteBaseSerializer(SubmissionWriteBaseSerializer):
    target_submission_id = serializers.UUIDField(
        required=False, allow_null=True, write_only=True
    )

    def validate_target_submission_id(self, value):
        if value is None:
            return value
        if not Submission.objects.filter(id=value).exists():
            raise serializers.ValidationError(
                "Invalid target_submission_id. Submission not found."
            )
        return value

    def _inject_target_submission(self, validated_data):
        target_submission_id = validated_data.pop(
            "target_submission_id", serializers.empty
        )
        if target_submission_id is serializers.empty:
            return validated_data

        if target_submission_id is None:
            validated_data["target_content_type"] = None
            validated_data["target_object_id"] = None
            return validated_data

        validated_data["target_content_type"] = ContentType.objects.get_for_model(
            Submission
        )
        validated_data["target_object_id"] = target_submission_id
        return validated_data

    def create(self, validated_data):
        validated_data = self._inject_target_submission(validated_data)
        return super().create(validated_data)

    def update(self, instance, validated_data):
        validated_data = self._inject_target_submission(validated_data)
        return super().update(instance, validated_data)


# ==========================================
# Import Submission Serializers
# ==========================================


class ImportSubmissionReadSerializer(SubmissionBaseSerializer):
    commodity_type_detail = CommodityTypeSerializer(
        source="commodity_type", read_only=True
    )
    unit_of_measurement_detail = UnitOfMeasurementSerializer(
        source="unit_of_measurement", read_only=True
    )

    class Meta(SubmissionBaseSerializer.Meta):
        model = ImportSubmission
        fields = SubmissionBaseSerializer.Meta.fields + (
            "commodity_type",
            "commodity_type_detail",
            "detailed_description",
            "approval_no",
            "approval_time",
            "import_date",
            "importer_name",
            "importer_address",
            "importer_contact",
            "exporter_name",
            "exporter_address",
            "exporter_contact",
            "total_quantity",
            "bag_packet_count",
            "manufacturing_date",
            "batch_no",
            "base_amount",
            "amount_in_npr",
            "currency",
            "health_certificate_no",
            "health_certificate_issue_date",
            "health_certificate_issue_agency",
            "invoice_no",
            "invoice_date",
            "lab_testing_info",
            "origin_of_consignment",
            "unit_of_measurement",
            "unit_of_measurement_detail",
        )


class ImportSubmissionWriteSerializer(SubmissionWriteBaseSerializer):
    amount_in_npr = serializers.DecimalField(read_only=True, max_digits=15, decimal_places=2)

    class Meta:
        model = ImportSubmission
        exclude = (
            "submitted_at",
            "status",
            "created_by",
            "updated_by",
            "nepali_year",
            "nepali_month",
            "nepali_day",
            "nepali_quarter",
            "import_nepali_year",
            "import_nepali_month",
            "import_nepali_day",
            "import_nepali_quarter",
        )

    def create(self, validated_data):
        # Tracking code is auto-generated in model save()
        return super().create(validated_data)


class ExportSubmissionReadSerializer(SubmissionBaseSerializer):
    commodity_type_detail = CommodityTypeSerializer(
        source="commodity_type", read_only=True
    )
    unit_of_measurement_detail = UnitOfMeasurementSerializer(
        source="unit_of_measurement", read_only=True
    )

    class Meta(SubmissionBaseSerializer.Meta):
        model = ExportSubmission
        fields = SubmissionBaseSerializer.Meta.fields + (
            "commodity_type",
            "commodity_type_detail",
            "exporter_name",
            "destination_country",
            "export_date",
            "quantity",
            "goods_description",
            "base_amount",
            "amount_in_npr",
            "currency",
            "unit_of_measurement",
            "unit_of_measurement_detail",
        )


class ExportSubmissionWriteSerializer(SubmissionWriteBaseSerializer):
    amount_in_npr = serializers.DecimalField(read_only=True, max_digits=15, decimal_places=2)

    class Meta:
        model = ExportSubmission
        exclude = (
            "submitted_at",
            "status",
            "created_by",
            "updated_by",
            "nepali_year",
            "nepali_month",
            "nepali_day",
            "nepali_quarter",
            "export_nepali_year",
            "export_nepali_month",
            "export_nepali_day",
            "export_nepali_quarter",
        )


class RevenueFineSubmissionReadSerializer(SubmissionBaseSerializer):
    collection_type_detail = CollectionTypeSerializer(
        source="collection_type", read_only=True
    )

    class Meta(SubmissionBaseSerializer.Meta):
        model = RevenueFineSubmission
        fields = SubmissionBaseSerializer.Meta.fields + (
            "collection_type",
            "collection_type_detail",
            "target_content_type",
            "target_object_id",
            "revenue_date",
            "base_amount",
            "amount_in_npr",
            "currency",
            "payment_refrence",
            "reason",
        )


class RevenueFineSubmissionWriteSerializer(TargetSubmissionWriteBaseSerializer):
    amount_in_npr = serializers.DecimalField(read_only=True, max_digits=15, decimal_places=2)

    class Meta:
        model = RevenueFineSubmission
        exclude = (
            "submitted_at",
            "status",
            "created_by",
            "updated_by",
            "target_content_type",
            "target_object_id",
            "nepali_year",
            "nepali_month",
            "nepali_day",
            "nepali_quarter",
            "revenue_nepali_year",
            "revenue_nepali_month",
            "revenue_nepali_day",
            "revenue_nepali_quarter",
        )


class HealthInspectionSubmissionReadSerializer(SubmissionBaseSerializer):
    commodity_type_detail = CommodityTypeSerializer(
        source="commodity_type", read_only=True
    )
    unit_of_measurement_detail = UnitOfMeasurementSerializer(
        source="unit_of_measurement", read_only=True
    )

    class Meta(SubmissionBaseSerializer.Meta):
        model = HealthInspectionSubmission
        fields = SubmissionBaseSerializer.Meta.fields + (
            "commodity_type",
            "commodity_type_detail",
            "target_content_type",
            "target_object_id",
            "inspection_type",
            "product_name",
            "health_date",
            "quantity",
            "unit_of_measurement",
            "unit_of_measurement_detail",
            "result",
            "notes",
        )


class HealthInspectionSubmissionWriteSerializer(TargetSubmissionWriteBaseSerializer):
    class Meta:
        model = HealthInspectionSubmission
        exclude = (
            "submitted_at",
            "status",
            "created_by",
            "updated_by",
            "target_content_type",
            "target_object_id",
            "nepali_year",
            "nepali_month",
            "nepali_day",
            "nepali_quarter",
            "health_nepali_year",
            "health_nepali_month",
            "health_nepali_day",
            "health_nepali_quarter",
        )


class DestructionSubmissionReadSerializer(SubmissionBaseSerializer):
    commodity_type_detail = CommodityTypeSerializer(
        source="commodity_type", read_only=True
    )
    unit_of_measurement_detail = UnitOfMeasurementSerializer(
        source="unit_of_measurement", read_only=True
    )

    class Meta(SubmissionBaseSerializer.Meta):
        model = DestructionSubmission
        fields = SubmissionBaseSerializer.Meta.fields + (
            "commodity_type",
            "commodity_type_detail",
            "target_content_type",
            "target_object_id",
            "quantity",
            "unit_of_measurement",
            "unit_of_measurement_detail",
            "destruction_method",
            "destruction_date",
            "reason",
        )


class DestructionSubmissionWriteSerializer(TargetSubmissionWriteBaseSerializer):
    class Meta:
        model = DestructionSubmission
        exclude = (
            "submitted_at",
            "status",
            "created_by",
            "updated_by",
            "target_content_type",
            "target_object_id",
            "nepali_year",
            "nepali_month",
            "nepali_day",
            "nepali_quarter",
            "destruction_nepali_year",
            "destruction_nepali_month",
            "destruction_nepali_day",
            "destruction_nepali_quarter",
        )


class UnifiedSubmissionReadSerializer(SubmissionBaseSerializer):
    submission_kind = serializers.SerializerMethodField()
    import_submission = ImportSubmissionReadSerializer(
        source="importsubmission", read_only=True
    )
    export_submission = ExportSubmissionReadSerializer(
        source="exportsubmission", read_only=True
    )
    revenue_fine_submission = RevenueFineSubmissionReadSerializer(
        source="revenuefinesubmission", read_only=True
    )
    health_inspection_submission = HealthInspectionSubmissionReadSerializer(
        source="healthinspectionsubmission", read_only=True
    )
    destruction_submission = DestructionSubmissionReadSerializer(
        source="destructionsubmission", read_only=True
    )

    class Meta(SubmissionBaseSerializer.Meta):
        model = Submission
        fields = SubmissionBaseSerializer.Meta.fields + (
            "submission_kind",
            "import_submission",
            "export_submission",
            "revenue_fine_submission",
            "health_inspection_submission",
            "destruction_submission",
        )

    @extend_schema_field(serializers.CharField())
    def get_submission_kind(self, obj):
        relation_map = (
            ("importsubmission", "import"),
            ("exportsubmission", "export"),
            ("revenuefinesubmission", "revenue_fine"),
            ("healthinspectionsubmission", "health_inspection"),
            ("destructionsubmission", "destruction"),
        )
        for relation_name, kind in relation_map:
            try:
                related_obj = getattr(obj, relation_name)
            except ObjectDoesNotExist:
                continue
            if related_obj:
                return kind
        return "base"
