package utils

import (
	"context"
	"log"
	"math"
	"strings"

	"pluja.dev/kycnot.me/database"
	"pluja.dev/kycnot.me/ent"
)

func ComputeScore(s *ent.Service) int {
	const (
		goodRating    = 1
		warningRating = 2
		badRating     = 3
		maxTosPenalty = 2
		baseScore     = 10
		maxScore      = 10
		minScore      = 0
		bonusLow      = 0.25
		bonusMedium   = 0.75
		bonusHigh     = 1.5
	)

	attributes := strings.Split(s.Attributes, ",")
	grade := float64(baseScore)

	// Attribute Ratings
	for _, attr := range attributes {
		a := database.ServiceAttributes.GetAttribute(attr)
		switch a.Rating {
		case goodRating:
			grade += bonusLow
		case warningRating:
			grade -= bonusMedium
		case badRating:
			grade -= bonusHigh
		}
	}

	// Tos Highlights Penalty
	nTosH := len(*s.TosHighlights)
	nTosPenalty := float64(nTosH) * bonusMedium // Each 2 TOS highlights, decrease the score by 1
	if nTosPenalty > maxTosPenalty {
		nTosPenalty = maxTosPenalty
	}
	grade -= nTosPenalty

	// Cash/Monero Bonus
	if s.Cash || s.Xmr {
		grade += bonusLow
	}

	// KYC Level Adjustment
	switch s.KycLevel {
	case 0:
		grade += bonusMedium
	case 1:
		grade += bonusLow
	case 2:
		grade -= bonusMedium
	case 3:
		grade -= bonusHigh
	}

	// P2P/OpenSource Bonus
	if strings.Contains(s.Attributes, database.AttributeP2P) || strings.Contains(s.Attributes, database.AttributeOpenSource) {
		grade += bonusLow
	}

	// Tor URL Bonus
	if len(s.OnionUrls) > 0 && s.OnionUrls[0] != "" {
		grade += bonusLow
	}

	// Normalize the grade to be within 0-10 bounds
	grade = math.Min(maxScore, math.Max(minScore, grade))

	log.Printf("Grade: %.0f", grade)
	return int(math.Round(grade))
}

func UpdateScore(s *ent.Service) error {
	score := ComputeScore(s)
	ctx := context.Background()
	_, err := database.Client.Service.UpdateOneID(s.ID).SetScore(score).Save(ctx)
	return err
}
