package notify

import (
	"fmt"

	"gitea.hostxtra.co.uk/mrhid6/vantage/server/internal/models"
	"gitea.hostxtra.co.uk/mrhid6/vantage/shared/mail"
)

// TypeVuln marks an event whose subject is a batch of new vulnerability
// findings rather than a state transition. It exists so title() does not
// describe a digest as something going "DOWN".
const TypeVuln = "vulnerability"

// VulnDigest is one batch of newly opened findings, ready to send.
//
// One per rule per scan, never one per finding: a database refresh can open
// several hundred at once, and a message each would rate-limit the webhook or
// get the channel muted — either way the alerts stop being read.
type VulnDigest struct {
	InstanceName string
	RuleName     string
	Summary      string
	TopSeverity  string
	Count        int
	Rows         []mail.VulnDigestRow
	More         int
	DBAge        string
}

// DispatchVulnDigest delivers a digest over one channel.
//
// SMTP gets its own template so a vulnerability digest does not arrive dressed
// as a monitor alert. The other four transports carry short text, so they reuse
// the existing Event path rather than growing a second payload shape per
// transport.
func DispatchVulnDigest(ch models.NotificationChannel, d VulnDigest) error {
	if ch.Type == models.ChannelSMTP {
		to := ch.Config["to"]
		sender := mail.Sender{
			Host:     ch.Config["host"],
			Port:     ch.Config["port"],
			From:     ch.Config["from"],
			Username: ch.Config["username"],
			Password: ch.Config["password"],
		}
		if !sender.Enabled() || sender.Port == "" || to == "" {
			return fmt.Errorf("smtp: missing host/port/from/to")
		}
		return sender.SendVulnDigest(to, mail.VulnDigest{
			InstanceName: d.InstanceName,
			Count:        d.Count,
			TopSeverity:  d.TopSeverity,
			Summary:      d.Summary,
			Rows:         d.Rows,
			More:         d.More,
			DBAge:        d.DBAge,
		})
	}

	return Dispatch(ch, Event{
		MonitorName: d.Summary,
		Type:        TypeVuln,
		Message:     vulnLines(d),
	})
}

// vulnLines renders the finding list for the text-only transports, capped by
// whatever the caller already put in Rows.
func vulnLines(d VulnDigest) string {
	var s string
	for _, r := range d.Rows {
		fix := "no fix published"
		if r.FixedIn != "" {
			fix = "fixed in " + r.FixedIn
		}
		s += fmt.Sprintf("\n• %s (%s) — %s on %s, %s", r.CVEID, r.Severity, r.PackageName, r.ServerName, fix)
	}
	if d.More > 0 {
		s += fmt.Sprintf("\n…and %d more.", d.More)
	}
	return s
}
