package mailstore

import (
	"strings"

	"mailgoserver/internal/db"
)

// FilterAction is the outcome of evaluating a mailbox's filter rules against one
// incoming message.
type FilterAction struct {
	Folder   string // non-empty: store here instead of INBOX
	MarkRead bool
	Drop     bool // don't store at all
}

// ApplyRules evaluates a mailbox's filter rules in priority order and returns the
// first match's action (zero value if none match, meaning "store in INBOX, unread").
// headers should have "from"/"to"/"subject" keys — rules run at delivery time, before
// the message is encrypted and stored, so real header values are available, not just
// the plaintext cache columns used for fast IMAP listing.
func (s *Store) ApplyRules(mailboxID int64, headers map[string]string) (FilterAction, error) {
	rules, err := s.DB.ListRulesForMailbox(mailboxID)
	if err != nil {
		return FilterAction{}, err
	}
	for _, r := range rules {
		if !r.IsActive {
			continue
		}
		if !ruleMatches(r, headers) {
			continue
		}
		switch r.Action {
		case "move_to_folder":
			return FilterAction{Folder: r.ActionValue}, nil
		case "mark_as_spam":
			// Reuses the same Junk folder score-based quarantine already delivers
			// into (see smtpserver/session.go) — from the mailbox owner's
			// perspective it's the same "goes to Junk" outcome either way.
			return FilterAction{Folder: "Junk"}, nil
		case "delete":
			return FilterAction{Drop: true}, nil
		case "mark_read":
			return FilterAction{MarkRead: true}, nil
		}
	}
	return FilterAction{}, nil
}

// ruleMatches combines a rule's conditions per its match type: "all" requires every
// condition to match (AND), "any" requires at least one (OR).
func ruleMatches(r db.MailboxFilterRule, headers map[string]string) bool {
	conditions, matchType := r.Conditions()
	if matchType == "any" {
		for _, c := range conditions {
			if matchCondition(c.Op, headers[c.Field], c.Value) {
				return true
			}
		}
		return false
	}
	for _, c := range conditions {
		if !matchCondition(c.Op, headers[c.Field], c.Value) {
			return false
		}
	}
	return true
}

func matchCondition(op, value, target string) bool {
	value = strings.ToLower(value)
	target = strings.ToLower(target)
	switch op {
	case "contains":
		return strings.Contains(value, target)
	case "equals":
		return value == target
	case "starts_with":
		return strings.HasPrefix(value, target)
	}
	return false
}
