from rest_framework import serializers

from api.models import AlbumUser, Photo
from api.serializers.photos import GroupedPhotosSerializer
from api.serializers.PhotosGroupedByDate import get_photos_ordered_by_date
from api.serializers.simple import PhotoSuperSimpleSerializer, SimpleUserSerializer
from api.util import logger


class AlbumUserSerializer(serializers.ModelSerializer):
    id = serializers.SerializerMethodField()
    owner = SimpleUserSerializer(many=False, read_only=True)
    shared_to = SimpleUserSerializer(many=True, read_only=True)
    date = serializers.SerializerMethodField()
    location = serializers.SerializerMethodField()
    grouped_photos = serializers.SerializerMethodField()
    public = serializers.SerializerMethodField()
    public_slug = serializers.SerializerMethodField()
    public_expires_at = serializers.SerializerMethodField()
    public_sharing_options = serializers.SerializerMethodField()

    class Meta:
        model = AlbumUser
        fields = (
            "id",
            "title",
            "owner",
            "shared_to",
            "date",
            "location",
            "grouped_photos",
            "public",
            "public_slug",
            "public_expires_at",
            "public_sharing_options",
        )

    # To-Do: Legacy definition, should be a number instead
    def get_id(self, obj) -> str:
        return str(obj.id)

    def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True):
        grouped_photos = get_photos_ordered_by_date(
            obj.photos.all().order_by("-exif_timestamp")
        )
        res = GroupedPhotosSerializer(grouped_photos, many=True).data
        return res

    def get_location(self, obj) -> str:
        for photo in obj.photos.all():
            if (
                photo
                and hasattr(photo, "search_instance")
                and photo.search_instance
                and photo.search_instance.search_location
            ):
                return photo.search_instance.search_location
        return ""

    def get_date(self, obj) -> str:
        for photo in obj.photos.all():
            if photo and photo.exif_timestamp:
                return photo.exif_timestamp
        return ""

    def get_public(self, obj) -> bool:
        return bool(getattr(obj, "share", None) and obj.share.enabled)

    def get_public_slug(self, obj) -> str:
        return getattr(getattr(obj, "share", None), "slug", "") or ""

    def get_public_expires_at(self, obj):
        return getattr(getattr(obj, "share", None), "expires_at", None)

    def get_public_sharing_options(self, obj) -> dict | None:
        """Return the per-album sharing option overrides (None values = use default)."""
        share = getattr(obj, "share", None)
        if not share:
            return None
        return {
            "share_location": share.share_location,
            "share_camera_info": share.share_camera_info,
            "share_timestamps": share.share_timestamps,
            "share_captions": share.share_captions,
            "share_faces": share.share_faces,
        }


class AlbumUserEditSerializer(serializers.ModelSerializer):
    photos = serializers.PrimaryKeyRelatedField(
        many=True, read_only=False, queryset=Photo.objects.all()
    )
    removedPhotos = serializers.ListField(
        child=serializers.CharField(max_length=100, default=""),
        write_only=True,
        required=False,
    )

    class Meta:
        model = AlbumUser
        fields = (
            "id",
            "title",
            "photos",
            "created_on",
            "favorited",
            "removedPhotos",
            "cover_photo",
        )

    def create(self, validated_data):
        title = validated_data["title"]
        photos = validated_data["photos"]

        user = None
        request = self.context.get("request")
        if request and hasattr(request, "user"):
            user = request.user

        # check if an album exists with the given title and call the update method if it does
        instance, created = AlbumUser.objects.get_or_create(title=title, owner=user)
        if not created:
            return self.update(instance, validated_data)

        for photo in photos:
            instance.photos.add(photo)
        instance.save()
        logger.info(f"Created user album {instance.id} with {len(photos)} photos")
        return instance

    def update(self, instance, validated_data):
        if "title" in validated_data.keys():
            title = validated_data["title"]
            instance.title = title
            logger.info(f"Renamed user album to {title}")

        if "removedPhotos" in validated_data.keys():
            image_hashes = validated_data["removedPhotos"]
            photos_already_in_album = instance.photos.all()
            cnt = 0
            for obj in photos_already_in_album:
                if obj.image_hash in image_hashes:
                    cnt += 1
                    instance.photos.remove(obj)

            logger.info(f"Removed {cnt} photos from user album {instance.id}")

        if "cover_photo" in validated_data.keys():
            cover_photo = validated_data["cover_photo"]
            instance.cover_photo = cover_photo
            logger.info(f"Changed cover photo to {cover_photo}")

        if "photos" in validated_data.keys():
            photos = validated_data["photos"]
            photos_already_in_album = instance.photos.all()
            cnt = 0
            for photo in photos:
                if photo not in photos_already_in_album:
                    cnt += 1
                    instance.photos.add(photo)

            logger.info(f"Added {cnt} photos to user album {instance.id}")

        instance.save()
        return instance


class AlbumUserListSerializer(serializers.ModelSerializer):
    cover_photo = serializers.SerializerMethodField()
    photo_count = serializers.SerializerMethodField()
    shared_to = SimpleUserSerializer(many=True, read_only=True)
    owner = SimpleUserSerializer(many=False, read_only=True)
    public = serializers.SerializerMethodField()
    public_slug = serializers.SerializerMethodField()
    public_expires_at = serializers.SerializerMethodField()
    public_sharing_options = serializers.SerializerMethodField()

    class Meta:
        model = AlbumUser
        fields = (
            "id",
            "cover_photo",
            "created_on",
            "favorited",
            "title",
            "shared_to",
            "owner",
            "photo_count",
            "public",
            "public_slug",
            "public_expires_at",
            "public_sharing_options",
        )

    def get_cover_photo(self, obj) -> PhotoSuperSimpleSerializer:
        if obj.cover_photo:
            return PhotoSuperSimpleSerializer(obj.cover_photo).data
        return PhotoSuperSimpleSerializer(obj.photos.first()).data

    def get_photo_count(self, obj) -> int:
        try:
            return obj.photo_count
        except Exception:  # for when calling AlbumUserListSerializer(obj).data directly
            return obj.photos.count()

    def get_public(self, obj) -> bool:
        return bool(getattr(obj, "share", None) and obj.share.enabled)

    def get_public_slug(self, obj) -> str:
        return getattr(getattr(obj, "share", None), "slug", "") or ""

    def get_public_expires_at(self, obj):
        return getattr(getattr(obj, "share", None), "expires_at", None)

    def get_public_sharing_options(self, obj) -> dict | None:
        """Return the per-album sharing option overrides (None values = use default)."""
        share = getattr(obj, "share", None)
        if not share:
            return None
        return {
            "share_location": share.share_location,
            "share_camera_info": share.share_camera_info,
            "share_timestamps": share.share_timestamps,
            "share_captions": share.share_captions,
            "share_faces": share.share_faces,
        }


class AlbumUserPublicSerializer(serializers.ModelSerializer):
    """Serializer for publicly shared user albums.

    Ensures photos are grouped from a filtered subset (not hidden, not in trashcan).
    """

    id = serializers.SerializerMethodField()
    owner = SimpleUserSerializer(many=False, read_only=True)
    date = serializers.SerializerMethodField()
    location = serializers.SerializerMethodField()
    grouped_photos = serializers.SerializerMethodField()
    public_slug = serializers.CharField(read_only=True)
    public_expires_at = serializers.DateTimeField(read_only=True)

    class Meta:
        model = AlbumUser
        fields = (
            "id",
            "title",
            "owner",
            "date",
            "location",
            "grouped_photos",
            "public_slug",
            "public_expires_at",
        )

    def get_id(self, obj) -> str:
        return str(obj.id)

    def _filtered_photos(self, obj):
        # Public albums should not expose hidden or trash photos
        return obj.photos.filter(hidden=False, in_trashcan=False).order_by(
            "-exif_timestamp"
        )

    def get_grouped_photos(self, obj) -> GroupedPhotosSerializer(many=True):
        grouped_photos = get_photos_ordered_by_date(self._filtered_photos(obj))
        return GroupedPhotosSerializer(grouped_photos, many=True).data

    def get_location(self, obj) -> str:
        for photo in self._filtered_photos(obj):
            if (
                photo
                and hasattr(photo, "search_instance")
                and photo.search_instance
                and photo.search_instance.search_location
            ):
                return photo.search_instance.search_location
        return ""

    def get_date(self, obj) -> str:
        for photo in self._filtered_photos(obj):
            if photo and photo.exif_timestamp:
                return photo.exif_timestamp
        return ""
