package services

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"

	"neomovies-api/pkg/config"
	"neomovies-api/pkg/models"
)

type ReactionsService struct {
	db     *mongo.Database
	client *http.Client
}

func NewReactionsService(db *mongo.Database) *ReactionsService {
	return &ReactionsService{
		db:     db,
		client: &http.Client{},
	}
}

var validReactions = []string{"fire", "nice", "think", "bore", "shit"}

// Получить счетчики реакций для медиа из внешнего API (cub.rip)
func (s *ReactionsService) GetReactionCounts(mediaType, mediaID string) (*models.ReactionCounts, error) {
	cubID := fmt.Sprintf("%s_%s", mediaType, mediaID)

	resp, err := s.client.Get(fmt.Sprintf("%s/reactions/get/%s", config.CubAPIBaseURL, cubID))
	if err != nil {
		return &models.ReactionCounts{}, nil
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return &models.ReactionCounts{}, nil
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return &models.ReactionCounts{}, nil
	}

	var response struct {
		Result []struct {
			Type    string `json:"type"`
			Counter int    `json:"counter"`
		} `json:"result"`
	}

	if err := json.Unmarshal(body, &response); err != nil {
		return &models.ReactionCounts{}, nil
	}

	counts := &models.ReactionCounts{}
	for _, reaction := range response.Result {
		switch reaction.Type {
		case "fire":
			counts.Fire = reaction.Counter
		case "nice":
			counts.Nice = reaction.Counter
		case "think":
			counts.Think = reaction.Counter
		case "bore":
			counts.Bore = reaction.Counter
		case "shit":
			counts.Shit = reaction.Counter
		}
	}

	return counts, nil
}

func (s *ReactionsService) GetMyReaction(userID, mediaType, mediaID string) (string, error) {
	collection := s.db.Collection("reactions")
	ctx := context.Background()

	var result struct {
		Type string `bson:"type"`
	}
	err := collection.FindOne(ctx, bson.M{
		"userId":    userID,
		"mediaType": mediaType,
		"mediaId":   mediaID,
	}).Decode(&result)
	if err != nil {
		if err == mongo.ErrNoDocuments {
			return "", nil
		}
		return "", err
	}
	return result.Type, nil
}

func (s *ReactionsService) SetReaction(userID, mediaType, mediaID, reactionType string) error {
	if !s.isValidReactionType(reactionType) {
		return fmt.Errorf("invalid reaction type")
	}

	collection := s.db.Collection("reactions")
	ctx := context.Background()

	_, err := collection.UpdateOne(
		ctx,
		bson.M{"userId": userID, "mediaType": mediaType, "mediaId": mediaID},
		bson.M{"$set": bson.M{"type": reactionType, "updatedAt": time.Now()}},
		options.Update().SetUpsert(true),
	)
	if err == nil {
		go s.sendReactionToCub(fmt.Sprintf("%s_%s", mediaType, mediaID), reactionType)
	}
	return err
}

func (s *ReactionsService) RemoveReaction(userID, mediaType, mediaID string) error {
	collection := s.db.Collection("reactions")
	ctx := context.Background()

	_, err := collection.DeleteOne(ctx, bson.M{
		"userId":    userID,
		"mediaType": mediaType,
		"mediaId":   mediaID,
	})

	fullMediaID := fmt.Sprintf("%s_%s", mediaType, mediaID)
	go s.sendReactionToCub(fullMediaID, "remove")

	return err
}

// Получить все реакции пользователя
func (s *ReactionsService) GetUserReactions(userID string, limit int) ([]models.Reaction, error) {
	collection := s.db.Collection("reactions")

	ctx := context.Background()
	cursor, err := collection.Find(ctx, bson.M{"userId": userID})
	if err != nil {
		return nil, err
	}
	defer cursor.Close(ctx)

	var reactions []models.Reaction
	if err := cursor.All(ctx, &reactions); err != nil {
		return nil, err
	}

	return reactions, nil
}

func (s *ReactionsService) isValidReactionType(reactionType string) bool {
	for _, valid := range validReactions {
		if valid == reactionType {
			return true
		}
	}
	return false
}

// Отправка реакции в cub.rip API (асинхронно)
func (s *ReactionsService) sendReactionToCub(mediaID, reactionType string) {
	url := fmt.Sprintf("%s/reactions/set", config.CubAPIBaseURL)

	data := map[string]string{
		"mediaId": mediaID,
		"type":    reactionType,
	}

	_, err := json.Marshal(data)
	if err != nil {
		return
	}

	resp, err := s.client.Get(fmt.Sprintf("%s?mediaId=%s&type=%s", url, mediaID, reactionType))
	if err != nil {
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusOK {
		fmt.Printf("Reaction sent to cub.rip: %s - %s\n", mediaID, reactionType)
	}
}
