from rest_framework import serializers
from recipes.models import Information, Review, Ingredient, RawIngredient, Unit
from profiles.models import Account, Favorite, Pantry
from django.db.models import Avg, Count
from django.template.defaultfilters import date
from decimal import Decimal
import ast
import math
from profiles.utils import get_owned_raw_ingredients, get_owned_recipe_ingredients


class RawIngredientSerializer(serializers.ModelSerializer):
    class Meta:
        model = RawIngredient
        fields = '__all__'


class RecipeIngredientsSerializer(serializers.ModelSerializer):
    unit = serializers.CharField(source='unit.code')

    class Meta:
        model = Ingredient
        fields = ('id', 'name', 'amount', 'state', 'recipe', 'unit')


class ReviewSummarySerializer(serializers.ModelSerializer):
    score = serializers.SerializerMethodField()
    amount = serializers.SerializerMethodField()

    class Meta:
        model = Review
        fields = ('score', 'amount')

    def get_score(self, instance):
        ratings = Review.objects.filter(recipe_id=instance.id).aggregate(
            avg_rating=Avg('rating'),
            review_count=Count('id')
        )
        avg_rating = ratings['avg_rating'] or 0
        review_count = ratings['review_count'] or 0

        total_rating = avg_rating * review_count
        total_review_count = review_count

        return Decimal(total_rating / total_review_count).quantize(Decimal('0.00')) if total_review_count != 0 else 0.0

    def get_amount(self, instance):
        review_counts = Review.objects.filter(recipe_id=instance.id).aggregate(
            user_review_count=Count('id')
        )
        user_review_count = review_counts['user_review_count'] or 0

        total_review_count = user_review_count

        return total_review_count


class RecipeInformationSerializer(serializers.ModelSerializer):
    completeness_percentage = serializers.SerializerMethodField()
    is_favorited = serializers.SerializerMethodField()
    similarity = serializers.SerializerMethodField()
    rating = ReviewSummarySerializer(source='*')

    def get_is_favorited(self, instance):
        user = self.context['request'].user
        if user.is_authenticated:
            return Favorite.objects.filter(recipe_id=instance.pk, user=user).exists()
        return False

    def get_completeness_percentage(self, instance):
        user = self.context['request'].user

        if user.is_authenticated:
            owned_ingredient_ids = len(get_owned_recipe_ingredients(
                user, instance.pk))

            total_ingredient_count = instance.ingredients.count()
            # print(instance.title + " " + str(owned_ingredient_ids) + " " + str(total_ingredient_count))            

            if total_ingredient_count > 0:
                completeness_percentage = (
                    owned_ingredient_ids / total_ingredient_count * 100
                )
                completeness_percentage = round(
                    completeness_percentage, 2)
                return completeness_percentage                

        return 0

    def get_similarity(self, instance):  # Add this method
        recipe_id = instance.pk
        top_recipes = self.context.get('top_recipes')
        similarity = 0

        if top_recipes:
            similarity = next(
                (item['similarity']
                 for item in top_recipes if item['recipe_id'] == recipe_id),
                0
            )
        similarity = round(Decimal(similarity), 2)
        return similarity

    class Meta:
        model = Information
        fields = ('id', 'title', 'image_url', 'difficulty', 'source_url',
                  'calories', 'completeness_percentage', 'is_favorited', 'rating', 'similarity')


class RecipeDetailSerializer(serializers.ModelSerializer):
    ingredients = serializers.SerializerMethodField()
    owned_ingredients = serializers.SerializerMethodField()
    not_owned_ingredients = serializers.SerializerMethodField()
    instructions = serializers.CharField()
    rating = ReviewSummarySerializer(source='*')
    is_favorited = serializers.SerializerMethodField()

    def get_ingredients(self, instance):
        user = self.context['request'].user

        all_ingredients = Ingredient.objects.filter(recipe=instance)
        serialized_data = RecipeIngredientsSerializer(
            all_ingredients, many=True).data

        if user.is_authenticated:
            owned_ingredient_ids = get_owned_recipe_ingredients(
                user, instance.pk)
            owned_ingredients = Ingredient.objects.filter(
                pk__in=owned_ingredient_ids).distinct()

            owned_ids = [ingredient.id for ingredient in owned_ingredients]
            for ingredient in serialized_data:
                if ingredient['id'] in owned_ids:

                    ingredient['is_owned'] = True
                else:
                    ingredient['is_owned'] = False
        else:
            for ingredient in serialized_data:
                ingredient['is_owned'] = False

        return serialized_data

    def get_owned_ingredients(self, instance):
        user = self.context['request'].user

        if not user.is_authenticated:
            return []

        owned_ingredient_ids = get_owned_recipe_ingredients(
            user, instance.pk)
        owned_ingredients = Ingredient.objects.filter(
            id__in=owned_ingredient_ids).distinct()
        return RecipeIngredientsSerializer(owned_ingredients, many=True).data

    def get_not_owned_ingredients(self, instance):
        user = self.context['request'].user

        if not user.is_authenticated:
            all_ingredients = Ingredient.objects.filter(recipe=instance)
            return RecipeIngredientsSerializer(all_ingredients, many=True).data

        owned_raw_ingredient_ids = Pantry.objects.filter(
            user=user).values_list('raw_ingredient_id', flat=True)
        not_owned_ingredients = Ingredient.objects.filter(
            recipe=instance).exclude(raw_ingredient__in=owned_raw_ingredient_ids)

        return RecipeIngredientsSerializer(not_owned_ingredients, many=True).data

    def get_is_favorited(self, instance):
        user = self.context['request'].user
        if user.is_authenticated:
            return Favorite.objects.filter(recipe_id=instance.pk, user=user).exists()
        return False

    class Meta:
        model = Information
        fields = ('id', 'title', 'image_url', 'time', 'servings', 'difficulty', 'source_url',
                  'rating', 'is_favorited', 'ingredients', 'instructions', 'owned_ingredients', 'not_owned_ingredients')

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        string_array = representation['instructions']
        array = ast.literal_eval(string_array)
        representation['instructions'] = array
        return representation


class ReviewSerializer(serializers.ModelSerializer):
    user = serializers.SerializerMethodField()
    modified_date = serializers.SerializerMethodField()

    class Meta:
        model = Review
        fields = ('id', 'user', 'rating', 'comment', 'modified_date')

    def get_user(self, instance):
        account = Account.objects.get(user=instance.user)
        user_data = {
            'id': account.user.id,
            'profile_picture': account.profile_picture.url,
            'name': f"{account.user.first_name} {account.user.last_name}"
        }
        return user_data

    def get_modified_date(self, instance):
        modified_date = date(instance.updated_at, "j F Y")
        return modified_date


class UnitSerializer(serializers.ModelSerializer):
    class Meta:
        model = Unit
        fields = '__all__'
