package services

import (
	"fmt"
	"regexp"
	"sort"
	"strings"
	"time"
)

type answerSourceHint struct {
	SourceID uint   `json:"source_id"`
	Question string `json:"question"`
}

var answerReferenceURLPattern = regexp.MustCompile("(?i)https?://[^\\s<>\"`]+")

func unsupportedAnswerNumbers(answer, evidence, references string) []string {
	// A URL identifier is not a price/version claim. Only an exact URL from
	// the selected evidence may bypass numeric checking; its path can never
	// prove an amount in the surrounding prose.
	knownURLs := map[string]bool{}
	trimURL := func(raw string) string { return strings.TrimRight(raw, ".,;:!?）】」』\"')]") }
	for _, raw := range answerReferenceURLPattern.FindAllString(evidence+"\n"+references, -1) {
		knownURLs[trimURL(raw)] = true
	}
	answer = answerReferenceURLPattern.ReplaceAllStringFunc(answer, func(raw string) string {
		if knownURLs[trimURL(raw)] {
			return " "
		}
		return raw
	})
	supported := strictAnswerNumbers(answerReferenceURLPattern.ReplaceAllString(evidence, " "))
	missing := []string{}
	for number := range strictAnswerNumbers(listPrefixPattern.ReplaceAllString(answer, "")) {
		if !supported[number] {
			missing = append(missing, number)
		}
	}
	sort.Strings(missing)
	return missing
}

// Hints help the agent repair a selection, but never add evidence implicitly.
// Only already-read sources from this assistant are eligible. Any source the
// agent selects next still goes through publication, version and meaning checks.
func (s *agentTurnState) explainNumberGap(agentID uint, missing []string, result answerCheckResult) answerCheckResult {
	result.UnsupportedNumbers = missing[:min(len(missing), 12)]
	result.Guidance += " Angka yang belum didukung: " + strings.Join(result.UnsupportedNumbers, ", ") + ". Cari bukti nama/versi dan maknanya, jangan sekadar menghapus nomor versi produk."
	now := time.Now()
	seen := map[uint]bool{}
	for _, source := range s.knowledge {
		if source.AgentID != agentID || !source.Active || source.ReviewStatus != "published" || seen[source.ID] ||
			(source.EffectiveFrom != nil && source.EffectiveFrom.After(now)) ||
			(source.EffectiveUntil != nil && !source.EffectiveUntil.After(now)) {
			continue
		}
		numbers := strictAnswerNumbers(source.Question + "\n" + source.Answer)
		for _, number := range missing {
			if numbers[number] {
				seen[source.ID] = true
				result.SourceHints = append(result.SourceHints, answerSourceHint{SourceID: source.ID, Question: clipContextText(source.Question, 180)})
				break
			}
		}
		if len(result.SourceHints) == 5 {
			break
		}
	}
	if len(result.SourceHints) > 0 {
		ids := make([]string, 0, len(result.SourceHints))
		for _, hint := range result.SourceHints {
			ids = append(ids, fmt.Sprint(hint.SourceID))
		}
		result.Guidance += " Sumber yang sudah ditemukan dan memuat angka tersebut: " + strings.Join(ids, ", ") + ". Periksa source_hints dan isi sumber; pilih source_ids hanya jika mendukung klaim yang sama. Kesamaan angka saja bukan bukti."
	} else {
		result.Guidance += " Cari ulang search_knowledge dengan nama produk dan angka yang dimaksud; bila tetap tidak ada, hilangkan klaim yang tidak didukung atau minta klarifikasi."
	}
	return result
}
