package discovery

import (
	"context"
	"sort"
	"time"
)

func (s *Service) fillSignaturesFromStoredMessages(ctx context.Context, email string, agg *addressAgg) {
	if len(agg.fromMessageRefs) == 0 {
		return
	}
	ids := make([]string, 0, len(agg.fromMessageRefs))
	for _, ref := range agg.fromMessageRefs {
		ids = append(ids, ref.id)
	}

	rows, err := s.db.Query(ctx, `
		SELECT id::text,
		       LEFT(COALESCE(body_text, ''), $2),
		       LEFT(COALESCE(body_html, ''), $2),
		       date
		FROM messages
		WHERE id = ANY($1::uuid[])
		ORDER BY date DESC
		LIMIT $3
	`, ids, maxBodyChars, maxSignatureMsgsPerEmail)
	if err != nil {
		return
	}
	defer rows.Close()

	agg.Signatures = agg.Signatures[:0]
	for rows.Next() {
		var id, bodyText, bodyHTML string
		var msgDate time.Time
		if err := rows.Scan(&id, &bodyText, &bodyHTML, &msgDate); err != nil {
			continue
		}
		sigText, conf := extractSignature(bodyText, bodyHTML, email, agg.DisplayName)
		if sigText == "" {
			continue
		}
		agg.Signatures = append(agg.Signatures, signatureCandidate{
			MessageID:     id,
			SignatureText: sigText,
			MessageDate:   msgDate,
			Confidence:    conf,
		})
	}
	sort.Slice(agg.Signatures, func(i, j int) bool {
		return agg.Signatures[i].MessageDate.After(agg.Signatures[j].MessageDate)
	})
	if len(agg.Signatures) > 5 {
		agg.Signatures = agg.Signatures[:5]
	}
}

func (s *Service) fillSignaturesForEmail(ctx context.Context, externalUserID, email string, agg *addressAgg) {
	if len(agg.fromMessageRefs) > 0 {
		s.fillSignaturesFromStoredMessages(ctx, email, agg)
		return
	}
	rows, err := s.db.Query(ctx, `
		SELECT m.id::text,
		       LEFT(COALESCE(m.body_text, ''), $3),
		       LEFT(COALESCE(m.body_html, ''), $3),
		       m.date
		FROM messages m
		JOIN mail_accounts ma ON m.account_id = ma.id
		WHERE ma.user_id = (SELECT id FROM users WHERE external_id = $1)
		  AND EXISTS (
		    SELECT 1 FROM jsonb_array_elements(m.from_addr) a
		    WHERE lower(coalesce(a->>'address', '')) = lower($2)
		  )
		ORDER BY m.date DESC
		LIMIT $4
	`, externalUserID, email, maxBodyChars, maxSignatureMsgsPerEmail)
	if err != nil {
		return
	}
	defer rows.Close()

	for rows.Next() {
		var id, bodyText, bodyHTML string
		var msgDate time.Time
		if err := rows.Scan(&id, &bodyText, &bodyHTML, &msgDate); err != nil {
			continue
		}
		sigText, conf := extractSignature(bodyText, bodyHTML, email, agg.DisplayName)
		if sigText == "" {
			continue
		}
		agg.Signatures = append(agg.Signatures, signatureCandidate{
			MessageID:     id,
			SignatureText: sigText,
			MessageDate:   msgDate,
			Confidence:    conf,
		})
	}
	sort.Slice(agg.Signatures, func(i, j int) bool {
		return agg.Signatures[i].MessageDate.After(agg.Signatures[j].MessageDate)
	})
	if len(agg.Signatures) > 5 {
		agg.Signatures = agg.Signatures[:5]
	}
}

type enrichCandidate struct {
	email string
	agg   *addressAgg
}

func selectPreEnrichCandidates(aggs map[string]*addressAgg, limit int) []enrichCandidate {
	var candidates []enrichCandidate
	for email, agg := range aggs {
		isML, isDisp, isSpamHeavy, _ := classifyAddress(agg)
		if isML || isDisp || isSpamHeavy {
			continue
		}
		if agg.MessageCount < minMessagesForLLM {
			continue
		}
		if _, ok := agg.Roles["from"]; !ok {
			continue
		}
		candidates = append(candidates, enrichCandidate{email: email, agg: agg})
	}
	sort.Slice(candidates, func(i, j int) bool {
		return candidates[i].agg.MessageCount > candidates[j].agg.MessageCount
	})
	if len(candidates) > limit {
		candidates = candidates[:limit]
	}
	return candidates
}

func enrichCandidateSet(candidates []enrichCandidate) map[string]bool {
	out := make(map[string]bool, len(candidates))
	for _, c := range candidates {
		out[c.email] = true
	}
	return out
}
