package utils

import (
	"fmt"
	"math"

	"github.com/rs/zerolog/log"

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

func ComputeScore(s *database.Service) int {
	const (
		baseScore   = 10
		maxScore    = 10
		minScore    = 0
		bonusLow    = 15
		bonusMedium = 20
		bonusHigh   = 25
	)

	grade := float64(baseScore)

	// KYC Level Adjustment
	switch s.KycLevel {
	case 0:
		grade = 8
	case 1:
		grade = 7
	case 2:
		grade = 6
	case 3:
		grade = 5
	}

	// Attributes
	for _, attr := range s.Expand["attributes"] {
		switch attr.Rating {
		case "good":
			grade += 0.1
		case "warn":
			grade -= 0.25
		case "bad":
			grade -= 0.5
		}
	}

	// Tos Highlights Penalty
	if s.TosReviews == nil {
		s.TosReviews = []database.TosReview{}
	}

	trPenalty := 0.0
	for _, tr := range s.TosReviews {
		if tr.Warning {
			trPenalty -= 0.1
		}
	}

	if trPenalty > 3 {
		trPenalty = 3
	}
	grade += float64(trPenalty)

	// Cash/Monero Bonus
	if s.Cash || s.Xmr || s.Btc {
		grade += 0.5
		if s.Xmr {
			grade += 0.25
		}
	}

	// TODO: Manage bonuses
	// P2P/OpenSource Bonus
	isP2P := false
	isOpenSource := false
	for _, attr := range s.Attributes {
		if attr == database.AttributeP2P {
			isP2P = true
		}
		if attr == database.AttributeOpenSource {
			isOpenSource = true
		}
	}

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

	if s.Verified {
		if grade < 7.5 {
			grade += 0.5
		}
	}

	if !isP2P && grade > 9.5 {
		grade = 9
	}

	if isP2P || isOpenSource && grade < 8 {
		grade += 0.5
	}

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

	log.Debug().Msgf("Computed score for %s: %f", s.Name, math.RoundToEven(grade))

	return int(math.RoundToEven(grade))
}

func ScoreSummary(s *database.Service) string {
	summary := fmt.Sprintf("SCORE BREAKDOWN - %s:\n", s.Name)
	const leftPad = 10 // Adjust this based on your longest line

	// KYC Level Adjustment
	switch s.KycLevel {
	case 0:
		summary += fmt.Sprintf("\n%-*s--> base score for KYC Level 0", leftPad, "|| 8")
	case 1:
		summary += fmt.Sprintf("\n%-*s--> base score for KYC Level 1", leftPad, "|| 7")
	case 2:
		summary += fmt.Sprintf("\n%-*s--> base score for KYC Level 2", leftPad, "|| 6")
	case 3:
		summary += fmt.Sprintf("\n%-*s--> base score for KYC Level 3", leftPad, "|| 5")
	}

	// Attributes
	for _, attr := range s.Expand["attributes"] {
		switch attr.Rating {
		case "good":
			summary += fmt.Sprintf("\n%-*s--> good attribute.", leftPad, "|| +0.1")
		case "warn":
			summary += fmt.Sprintf("\n%-*s--> warn attribute.", leftPad, "|| -0.25")
		case "bad":
			summary += fmt.Sprintf("\n%-*s--> bad attribute.", leftPad, "|| -0.5")
		}
	}

	// Tos Highlights Penalty (Corrected to reflect actual penalty logic)
	trPenalty := 0.0
	wrns := 0
	for _, tr := range s.TosReviews {
		if tr.Warning {
			trPenalty -= 0.1
			wrns++
		}
	}
	if trPenalty < -3 {
		trPenalty = -3
	}

	if trPenalty != 0 {
		summary += fmt.Sprintf("\n|| %-*f--> %d terms of service warnings.", leftPad, trPenalty, wrns)
	}

	// Cash/Monero Bonus
	if s.Cash || s.Xmr || s.Btc {
		summary += fmt.Sprintf("\n%-*s--> accepting Cash/BTC.", leftPad, "|| +0.5")
		if s.Xmr {
			summary += fmt.Sprintf("\n%-*s--> monero bonus.", leftPad, "|| +0.25")
		}
	}

	// Tor URL Bonus
	if len(s.OnionUrls) > 0 && s.OnionUrls[0] != "" {
		summary += fmt.Sprintf("\n%-*s--> having a Tor URL.", leftPad, "|| +0.5")
	}

	// Verified Bonus
	if s.Verified {
		summary += fmt.Sprintf("\n%-*s--> being verified (applies only if score was below 7.5 at this point).", leftPad, "|| +0.5")
	}

	// P2P/OpenSource Bonus
	// Note: Since the original ComputeScore doesn't show the exact bonus amount for P2P/OpenSource,
	// it's assumed to follow the similar logic as in ComputeScore for simplicity.
	isP2P := false
	isOpenSource := false
	for _, attr := range s.Attributes {
		if attr == database.AttributeP2P {
			isP2P = true
		}
		if attr == database.AttributeOpenSource {
			isOpenSource = true
		}
	}
	if isP2P || isOpenSource {
		summary += fmt.Sprintf("\n%-*s--> P2P/OpenSource bonus (applies if score was below 8).", leftPad, "|| +0.5")
	}

	summary += "\n||"
	summary += fmt.Sprintf("\n|| Total: %v", ComputeScore(s)) // Call ComputeScore to get the actual score
	summary += "\n(final score is rounded to even number)"

	return summary
}

/*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
}*/
