package mailrelay

import (
	"bytes"
	"fmt"
	"net/mail"
	"strings"
)

// Policy enforces the From-header domain allowlist.
//
// Per the spike (§5/§9) the check is on the From *header* domain, not the SMTP
// envelope MAIL FROM — Resend itself validates the header domain (the unverified-
// domain 550 lands at DATA, after MAIL/RCPT are accepted), so the relay must check
// the same thing. The recommendation is validate-and-REJECT (never silently rewrite),
// so a misconfigured app surfaces loudly instead of having its From re-stamped.
type Policy struct {
	allowed map[string]bool
}

// NewPolicy builds a From-domain allowlist (case-insensitive). An empty list
// defaults to {"felhom.eu"} (the one verified Resend domain).
func NewPolicy(domains []string) *Policy {
	p := &Policy{allowed: make(map[string]bool)}
	if len(domains) == 0 {
		domains = []string{"felhom.eu"}
	}
	for _, d := range domains {
		d = strings.ToLower(strings.TrimSpace(d))
		if d != "" {
			p.allowed[d] = true
		}
	}
	return p
}

// FromDomain parses the From *header* of a raw MIME message and returns the
// lowercased domain of the first address. It reads only the headers — the body is
// left untouched (the relay forwards raw bytes; this never re-serialises the message).
func FromDomain(raw []byte) (string, error) {
	msg, err := mail.ReadMessage(bytes.NewReader(raw))
	if err != nil {
		return "", fmt.Errorf("parsing message headers: %w", err)
	}
	from := strings.TrimSpace(msg.Header.Get("From"))
	if from == "" {
		return "", fmt.Errorf("missing From header")
	}
	addr, err := mail.ParseAddress(from)
	if err != nil {
		// Tolerate a From that is actually an address *list* (rare, but valid).
		list, lerr := mail.ParseAddressList(from)
		if lerr != nil || len(list) == 0 {
			return "", fmt.Errorf("parsing From address %q: %w", from, err)
		}
		addr = list[0]
	}
	at := strings.LastIndex(addr.Address, "@")
	if at < 0 || at == len(addr.Address)-1 {
		return "", fmt.Errorf("From address %q has no domain", addr.Address)
	}
	return strings.ToLower(addr.Address[at+1:]), nil
}

// Check returns nil iff the message's From-header domain is in the allowlist.
// A parse failure is treated as a rejection (fail-closed): an app that can't be
// parsed shouldn't reach Resend.
func (p *Policy) Check(raw []byte) error {
	dom, err := FromDomain(raw)
	if err != nil {
		return err
	}
	if !p.allowed[dom] {
		return fmt.Errorf("From domain %q not in allowlist", dom)
	}
	return nil
}

// Allowed reports whether a bare domain is in the allowlist (inspection/tests).
func (p *Policy) Allowed(domain string) bool {
	return p.allowed[strings.ToLower(strings.TrimSpace(domain))]
}
