from rest_framework import serializers
from .models import User, Tag, Attachment, Comment, Notification, AuditLog, AppConfig


class UserSerializer(serializers.ModelSerializer):
    """Serializer for User model. Includes RBAC roles + flat permissions."""
    is_superuser = serializers.BooleanField(read_only=True)
    roles = serializers.SerializerMethodField()
    permissions = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ["id", "email", "username", "first_name", "last_name", "avatar", "phone", "bio",
                  "must_change_password", "onboarding_completed", "is_superuser",
                  "roles", "permissions", "created_at", "updated_at"]
        read_only_fields = ["id", "must_change_password", "onboarding_completed",
                            "is_superuser", "roles", "permissions", "created_at", "updated_at"]

    def get_roles(self, obj):
        # Read from the prefetched user_roles__role cache (set on the viewset
        # queryset) — no inline select_related, which would bypass the cache
        # and re-query per row.
        return [
            {"id": ur.role.id, "slug": ur.role.slug, "name": ur.role.name}
            for ur in obj.user_roles.all()
        ]

    def get_permissions(self, obj):
        if obj.is_superuser:
            return ["*"]
        # Compute from the prefetched user_roles__role__permissions cache to
        # avoid a per-user query. Falls back to the model property when the
        # relations weren't prefetched (e.g. detail/create responses).
        if "user_roles" in getattr(obj, "_prefetched_objects_cache", {}):
            codes = {
                f"{p.domain}.{p.action}"
                for ur in obj.user_roles.all()
                for p in ur.role.permissions.all()
            }
            return sorted(codes)
        return sorted(obj.role_permission_codenames)


class TagSerializer(serializers.ModelSerializer):
    """Serializer for Tag model."""

    class Meta:
        model = Tag
        fields = ["id", "slug", "name", "color", "created_at"]
        read_only_fields = ["id", "created_at"]


class AttachmentSerializer(serializers.ModelSerializer):
    """Serializer for Attachment model."""

    class Meta:
        model = Attachment
        fields = ["id", "file", "name", "size", "mime_type", "uploaded_by", "created_at"]
        read_only_fields = ["id", "created_at"]


class CommentSerializer(serializers.ModelSerializer):
    """Serializer for Comment model."""
    author_name = serializers.SerializerMethodField()

    class Meta:
        model = Comment
        fields = ["id", "content", "author", "author_name", "created_at", "updated_at"]
        read_only_fields = ["id", "created_at", "updated_at"]

    def get_author_name(self, obj):
        return obj.author.get_full_name() or obj.author.username


class NotificationSerializer(serializers.ModelSerializer):
    """Serializer for Notification model."""

    class Meta:
        model = Notification
        fields = ["id", "user", "title", "message", "is_read", "link", "created_at"]
        read_only_fields = ["id", "created_at"]


class AuditLogSerializer(serializers.ModelSerializer):
    """Serializer for AuditLog model."""

    class Meta:
        model = AuditLog
        fields = ["id", "user", "action", "model_name", "object_id", "changes", "ip_address", "created_at"]
        read_only_fields = ["id", "created_at"]


class AppConfigSerializer(serializers.ModelSerializer):
    """System config. API keys + email password are write-only; a `*_set`
    boolean signals whether a secret is stored. Submitting a blank secret keeps
    the existing value (so the UI never has to round-trip the actual key)."""
    deepseek_api_key = serializers.CharField(write_only=True, required=False, allow_blank=True)
    minimax_api_key = serializers.CharField(write_only=True, required=False, allow_blank=True)
    email_password = serializers.CharField(write_only=True, required=False, allow_blank=True)

    deepseek_api_key_set = serializers.SerializerMethodField()
    minimax_api_key_set = serializers.SerializerMethodField()
    email_password_set = serializers.SerializerMethodField()

    class Meta:
        model = AppConfig
        fields = [
            "ai_provider",
            "deepseek_api_key", "deepseek_api_key_set", "deepseek_base_url", "deepseek_model",
            "minimax_api_key", "minimax_api_key_set", "minimax_base_url", "minimax_model",
            "email_host", "email_port", "email_username",
            "email_password", "email_password_set", "email_from", "email_use_tls",
            "calendar_default_view",
            "updated_at",
        ]
        read_only_fields = ["updated_at"]

    def get_deepseek_api_key_set(self, obj):
        return bool(obj.deepseek_api_key)

    def get_minimax_api_key_set(self, obj):
        return bool(obj.minimax_api_key)

    def get_email_password_set(self, obj):
        return bool(obj.email_password)

    def update(self, instance, validated_data):
        # Blank secret = leave unchanged.
        for secret in ("deepseek_api_key", "minimax_api_key", "email_password"):
            if secret in validated_data and validated_data[secret] == "":
                validated_data.pop(secret)
        return super().update(instance, validated_data)