// Package notice defines the announcement payload that is published as the
// signed static mirror of the API's /v1/notices endpoint (doc/05 §5 应急广播).
//
// Notices are read by the client's announcement slot and MUST be verified with
// the embedded Ed25519 public key before display, exactly like endpoint
// documents — they share the sign.Envelope.
package notice

import (
	"fmt"
	"strings"
)

// Valid severity levels.
const (
	LevelInfo     = "info"
	LevelWarning  = "warning"
	LevelCritical = "critical"
)

// Notice is a single bilingual announcement.
type Notice struct {
	ID          string `json:"id"`
	Level       string `json:"level"`
	TitleZH     string `json:"title_zh"`
	TitleEn     string `json:"title_en"`
	BodyZH      string `json:"body_zh"`
	BodyEn      string `json:"body_en"`
	URL         string `json:"url,omitempty"`
	PublishedAt string `json:"published_at"` // RFC3339 UTC
}

// List is the payload signed into a notices document.
type List struct {
	Notices []Notice `json:"notices"`
}

func validLevel(l string) bool {
	switch l {
	case LevelInfo, LevelWarning, LevelCritical:
		return true
	default:
		return false
	}
}

// Validate checks a single notice for completeness and a known severity.
func (n Notice) Validate() error {
	if strings.TrimSpace(n.ID) == "" {
		return fmt.Errorf("notice: id is required")
	}
	if !validLevel(n.Level) {
		return fmt.Errorf("notice %q: level must be one of info|warning|critical, got %q", n.ID, n.Level)
	}
	if strings.TrimSpace(n.TitleZH) == "" || strings.TrimSpace(n.TitleEn) == "" {
		return fmt.Errorf("notice %q: both title_zh and title_en are required", n.ID)
	}
	if strings.TrimSpace(n.PublishedAt) == "" {
		return fmt.Errorf("notice %q: published_at is required", n.ID)
	}
	return nil
}

// Validate checks the whole list and rejects duplicate IDs.
func (l List) Validate() error {
	seen := map[string]bool{}
	for _, n := range l.Notices {
		if err := n.Validate(); err != nil {
			return err
		}
		if seen[n.ID] {
			return fmt.Errorf("notice: duplicate id %q", n.ID)
		}
		seen[n.ID] = true
	}
	return nil
}
