package assignmentgen

import (
	"math"
	"sort"
	"strings"

	"boostai-backend/internal/sqlc"

	"github.com/jackc/pgx/v5/pgtype"
)

func buildWeaknessSummary(rows []sqlc.ListStudentPlanningPerformanceRow) WeaknessSummary {
	topicTotals := make(map[sqlc.QuestionTopic]float64)
	topicCounts := make(map[sqlc.QuestionTopic]int)
	tagStats := make(map[string]*tagWeaknessStats)
	recentIssues := make([]string, 0, 5)
	seenIssues := make(map[string]struct{})

	for _, row := range rows {
		score := planningScore(row.IsCorrect.Bool, numericFloat64OrZero(row.ReviewUnderstandingScore))
		accumulateTopicScore(topicTotals, topicCounts, row, score)
		accumulateTagStats(tagStats, row, score)
		recentIssues = appendRecentIssue(recentIssues, seenIssues, row.ReviewIssueReason.String)
		if len(recentIssues) >= 5 {
			break
		}
	}

	return WeaknessSummary{
		TopicScores:  buildTopicScores(topicTotals, topicCounts),
		WeakTags:     collectWeakTags(tagStats),
		RecentIssues: recentIssues,
	}
}

type tagWeaknessStats struct {
	total        float64
	count        int
	flaggedCount int
}

type scoredTag struct {
	tag          string
	score        float64
	flaggedCount int
}

func accumulateTopicScore(
	topicTotals map[sqlc.QuestionTopic]float64,
	topicCounts map[sqlc.QuestionTopic]int,
	row sqlc.ListStudentPlanningPerformanceRow,
	score float64,
) {
	if !row.Topic.Valid {
		return
	}

	topicTotals[row.Topic.QuestionTopic] += score
	topicCounts[row.Topic.QuestionTopic]++
}

func accumulateTagStats(tagStats map[string]*tagWeaknessStats, row sqlc.ListStudentPlanningPerformanceRow, score float64) {
	for _, rawTag := range row.QuestionTags {
		tag := strings.TrimSpace(strings.ToLower(rawTag))
		if tag == "" {
			continue
		}

		stats, ok := tagStats[tag]
		if !ok {
			stats = &tagWeaknessStats{}
			tagStats[tag] = stats
		}

		stats.total += score
		stats.count++
		if row.ReviewNeedsAttention {
			stats.flaggedCount++
		}
	}
}

func appendRecentIssue(recentIssues []string, seenIssues map[string]struct{}, rawIssue string) []string {
	issue := strings.TrimSpace(rawIssue)
	if issue == "" {
		return recentIssues
	}
	if _, exists := seenIssues[issue]; exists {
		return recentIssues
	}

	seenIssues[issue] = struct{}{}
	return append(recentIssues, issue)
}

func buildTopicScores(topicTotals map[sqlc.QuestionTopic]float64, topicCounts map[sqlc.QuestionTopic]int) map[sqlc.QuestionTopic]float64 {
	topicScores := make(map[sqlc.QuestionTopic]float64, len(topicTotals))
	for topic, total := range topicTotals {
		count := topicCounts[topic]
		if count == 0 {
			continue
		}
		topicScores[topic] = roundToOneDecimal((total / float64(count)) * 100)
	}
	return topicScores
}

func collectWeakTags(tagStats map[string]*tagWeaknessStats) []string {
	if len(tagStats) == 0 {
		return nil
	}

	weakCandidates := make([]scoredTag, 0, len(tagStats))
	for tag, stats := range tagStats {
		if stats == nil || stats.count == 0 {
			continue
		}
		average := (stats.total / float64(stats.count)) * 100
		if average >= 70 && stats.flaggedCount == 0 {
			continue
		}
		weakCandidates = append(weakCandidates, scoredTag{
			tag:          tag,
			score:        roundToOneDecimal(average),
			flaggedCount: stats.flaggedCount,
		})
	}

	sort.SliceStable(weakCandidates, func(i, j int) bool {
		if weakCandidates[i].score == weakCandidates[j].score {
			if weakCandidates[i].flaggedCount == weakCandidates[j].flaggedCount {
				return weakCandidates[i].tag < weakCandidates[j].tag
			}
			return weakCandidates[i].flaggedCount > weakCandidates[j].flaggedCount
		}
		return weakCandidates[i].score < weakCandidates[j].score
	})

	limit := 6
	if len(weakCandidates) < limit {
		limit = len(weakCandidates)
	}

	weakTags := make([]string, 0, limit)
	for idx := 0; idx < limit; idx++ {
		weakTags = append(weakTags, weakCandidates[idx].tag)
	}

	return weakTags
}

func planningScore(isCorrect bool, understandingScore float64) float64 {
	correctness := 0.0
	if isCorrect {
		correctness = 1.0
	}
	return (correctness + understandingScore) / 2
}

func roundToOneDecimal(value float64) float64 {
	return math.Round(value*10) / 10
}

func numericFloat64OrZero(value pgtype.Numeric) float64 {
	floatValue, err := value.Float64Value()
	if err != nil || !floatValue.Valid {
		return 0
	}
	return floatValue.Float64
}
