from rest_framework import serializers
from rest_framework.exceptions import AuthenticationFailed
from django.contrib.auth import get_user_model
from django.contrib.auth.password_validation import validate_password
from rest_framework_simplejwt.serializers import (
    TokenObtainPairSerializer,
    TokenRefreshSerializer,
)
from rest_framework_simplejwt.settings import api_settings
from checkpoint.models import CheckPoint, FacilityType

User = get_user_model()


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


class UserCheckPointSummarySerializer(serializers.ModelSerializer):
    facility = UserFacilityTypeSummarySerializer(source="facility_type", read_only=True)

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


class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
    """
    Customizes the JWT token to embed user-specific claims directly in the payload
    and enriches the login response JSON with user profile data.
    """

    @classmethod
    def get_token(cls, user):
        token = super().get_token(user)

        # Add custom claims inside the JWT Payload
        token["email"] = user.email
        token["role"] = user.role
        token["name"] = user.name

        return token

    def validate(self, attrs):
        username_value = attrs.get(self.username_field)
        if username_value:
            attrs[self.username_field] = User.objects.normalize_email(username_value)

        data = super().validate(attrs)
        if self.user.status != User.Status.ACTIVE:
            raise AuthenticationFailed(
                "User account is not active.",
                code="user_inactive",
            )

        # Add basic user info to the immediate login response body
        data["user"] = {
            "id": str(self.user.id),
            "email": self.user.email,
            "role": self.user.role,
            "status": self.user.status,
            "name": self.user.name,
        }

        return data


class CustomTokenRefreshSerializer(TokenRefreshSerializer):
    """
    Prevent suspended or inactive users from refreshing existing JWT sessions.
    """

    def validate(self, attrs):
        refresh = self.token_class(attrs["refresh"])
        user_id = refresh.payload.get(api_settings.USER_ID_CLAIM)

        if user_id is None:
            raise AuthenticationFailed(
                "User account is not active.",
                code="user_inactive",
            )

        try:
            user = User.objects.get(**{api_settings.USER_ID_FIELD: user_id})
        except User.DoesNotExist as exc:
            raise AuthenticationFailed(
                "User account is not active.",
                code="user_inactive",
            ) from exc

        if not user.is_active or user.status != User.Status.ACTIVE:
            raise AuthenticationFailed(
                "User account is not active.",
                code="user_inactive",
            )

        return super().validate(attrs)


class UserSerializer(serializers.ModelSerializer):
    """
    Standard serializer for retrieving user details.
    """

    checkpoint_detail = UserCheckPointSummarySerializer(
        source="checkpoint", read_only=True
    )

    class Meta:
        model = User
        fields = (
            "id",
            "email",
            "name",
            "checkpoint",
            "checkpoint_detail",
            "role",
            "status",
            "designation",
            "is_active",
            "is_staff",
            "created_at",
            "updated_at",
        )
        read_only_fields = ("id", "created_at", "updated_at", "is_active", "is_staff")


class UserCreateSerializer(serializers.ModelSerializer):
    """
    Serializer specifically tailored for user registration/creation.
    Ensures password is hashed properly and validated.
    """

    password = serializers.CharField(
        write_only=True,
        required=True,
        validators=[validate_password],
        style={"input_type": "password"},
    )
    password_confirm = serializers.CharField(
        write_only=True, required=True, style={"input_type": "password"}
    )
    role = serializers.ChoiceField(choices=User.Role.choices, required=False)
    checkpoint = serializers.PrimaryKeyRelatedField(
        queryset=CheckPoint.objects.all(),
        required=True,
        allow_null=False,
    )

    class Meta:
        model = User
        fields = (
            "id",
            "email",
            "name",
            "password",
            "password_confirm",
            "checkpoint",
            "role",
            "status",
            "designation",
        )

    def validate(self, attrs):
        if attrs["password"] != attrs["password_confirm"]:
            raise serializers.ValidationError(
                {"password": "Password fields didn't match."}
            )
        return attrs

    def validate_email(self, value):
        normalized_email = User.objects.normalize_email(value)
        if User.objects.filter(email__iexact=normalized_email).exists():
            raise serializers.ValidationError("A user with that email already exists.")
        return normalized_email

    def create(self, validated_data):
        validated_data.pop("password_confirm")
        # We don't save the raw password, CustomUserManager handles the hashing securely
        password = validated_data.pop("password")

        user = User.objects.create_user(password=password, **validated_data)
        return user


class UserUpdateSerializer(serializers.ModelSerializer):
    """
    Serializer for updating user profiles.
    Allows for optional secure password updating.
    """

    password = serializers.CharField(
        write_only=True,
        required=False,
        validators=[validate_password],
        style={"input_type": "password"},
    )

    class Meta:
        model = User
        fields = (
            "id",
            "email",
            "name",
            "password",
            "checkpoint",
            "role",
            "status",
            "designation",
        )
        read_only_fields = (
            "id",
            "email",
            "checkpoint",
            "role",
            "status",
        )

    def update(self, instance, validated_data):
        password = validated_data.pop("password", None)

        for attr, value in validated_data.items():
            setattr(instance, attr, value)

        if password:
            instance.set_password(password)

        instance.save()
        return instance


class UserAdminUpdateSerializer(serializers.ModelSerializer):
    """
    Serializer for admin-managed user updates from the staff management page.
    """

    checkpoint_detail = UserCheckPointSummarySerializer(
        source="checkpoint", read_only=True
    )
    role = serializers.ChoiceField(choices=User.Role.choices, required=False)
    status = serializers.ChoiceField(choices=User.Status.choices, required=False)

    class Meta:
        model = User
        fields = (
            "id",
            "email",
            "name",
            "checkpoint",
            "checkpoint_detail",
            "role",
            "status",
            "designation",
            "is_active",
            "is_staff",
            "created_at",
            "updated_at",
        )
        read_only_fields = (
            "id",
            "email",
            "checkpoint_detail",
            "is_active",
            "is_staff",
            "created_at",
            "updated_at",
        )

    def validate(self, attrs):
        role = attrs.get("role", getattr(self.instance, "role", None))
        checkpoint = attrs.get("checkpoint", getattr(self.instance, "checkpoint", None))

        if role == User.Role.STAFF and checkpoint is None:
            raise serializers.ValidationError(
                {"checkpoint": "Staff users must be assigned to a checkpoint."}
            )

        return attrs

    def update(self, instance, validated_data):
        for attr, value in validated_data.items():
            setattr(instance, attr, value)

        if "status" in validated_data:
            instance.is_active = validated_data["status"] == User.Status.ACTIVE

        instance.save()
        return instance


from .models import ActivityLog, AuditLog


class AuditLogListSerializer(serializers.ModelSerializer):
    user_email = serializers.EmailField(source="user.email", read_only=True)
    user_name = serializers.CharField(source="user.name", read_only=True)

    class Meta:
        model = AuditLog
        fields = (
            "id",
            "user",
            "user_email",
            "user_name",
            "action",
            "action_description",
            "method",
            "path",
            "status_code",
            "ip_address",
            "target_model",
            "target_object_id",
            "created_at",
        )
        read_only_fields = fields


class AuditLogDetailSerializer(AuditLogListSerializer):
    class Meta(AuditLogListSerializer.Meta):
        fields = AuditLogListSerializer.Meta.fields + (
            "payload",
            "query_params",
            "user_agent",
            "response_summary",
        )
        read_only_fields = fields


class ActivityLogListSerializer(serializers.ModelSerializer):
    actor_name = serializers.CharField(read_only=True)
    action_label = serializers.CharField(source="get_action_display", read_only=True)
    message = serializers.CharField(source="activity_message", read_only=True)
    target_label = serializers.CharField(read_only=True)
    occurred_at = serializers.DateTimeField(source="created_at", read_only=True)
    occurred_at_display = serializers.DateTimeField(
        source="created_at",
        format="%Y-%m-%d %H:%M:%S",
        read_only=True,
    )
    user_email = serializers.EmailField(source="user.email", read_only=True)

    class Meta:
        model = ActivityLog
        fields = (
            "id",
            "message",
            "actor_name",
            "user",
            "user_email",
            "action",
            "action_label",
            "target_model",
            "target_object_id",
            "target_label",
            "status_code",
            "occurred_at",
            "occurred_at_display",
        )
        read_only_fields = fields


class ActivityLogDetailSerializer(ActivityLogListSerializer):
    class Meta(ActivityLogListSerializer.Meta):
        fields = ActivityLogListSerializer.Meta.fields + (
            "action_description",
            "method",
            "path",
            "ip_address",
            "response_summary",
        )
        read_only_fields = fields
