// Package notify 内的 prefs.go 实现 per-user 通知偏好（notification_prefs 表）。
//
// 偏好控制 email / im 两条外发通道的开关与最低投递级别（min_severity）。in-app
// inbox（持久化 + SSE）始终投递，不受偏好影响。im_webhook_url 以密文落库
// （im_webhook_url_enc BYTEA，经 crypto.Encryptor 加密），repo 层只搬运密文，
// 解密由 IMNotifier 在发送时按需进行——避免明文 webhook URL 进入领域内存层。
package notify

import (
	"context"
	"errors"

	"github.com/google/uuid"
	"github.com/jackc/pgx/v5"
	"github.com/jackc/pgx/v5/pgxpool"

	"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
	notifysqlc "github.com/yan1h/agent-coding-workflow/internal/infra/notify/sqlc"
)

// Prefs 是一个用户的通知偏好领域形态。ImWebhookURLEnc 保持密文（BYTEA 原样），
// 解密延迟到 IMNotifier.Send。MinSeverity 取值与 Severity 枚举一致。
type Prefs struct {
	UserID          uuid.UUID
	EmailEnabled    bool
	IMEnabled       bool
	ImWebhookURLEnc []byte
	MinSeverity     Severity
}

// DefaultPrefs 是用户尚未显式设置偏好时的兜底：email / im 均关闭，最低级别
// warning（与 notification_prefs.min_severity 列默认值一致）。
func DefaultPrefs(userID uuid.UUID) Prefs {
	return Prefs{
		UserID:       userID,
		EmailEnabled: false,
		IMEnabled:    false,
		MinSeverity:  SeverityWarning,
	}
}

// PrefsRepository 抽象 notification_prefs 的读写。Get 在无记录时返回
// DefaultPrefs（不报 not-found），让上层无需区分 "未设置" 与 "全关"。
type PrefsRepository interface {
	Get(ctx context.Context, userID uuid.UUID) (Prefs, error)
	Upsert(ctx context.Context, p Prefs) error
}

// pgPrefsRepo 是 PrefsRepository 的 Postgres 实现。
type pgPrefsRepo struct {
	q *notifysqlc.Queries
}

// NewPostgresPrefsRepository 用现有 pgxpool 构造偏好仓库。
func NewPostgresPrefsRepository(pool *pgxpool.Pool) PrefsRepository {
	return &pgPrefsRepo{q: notifysqlc.New(pool)}
}

// Get 读取用户偏好。无记录时返回 DefaultPrefs(userID) 与 nil error。
func (r *pgPrefsRepo) Get(ctx context.Context, userID uuid.UUID) (Prefs, error) {
	row, err := r.q.GetNotificationPrefs(ctx, toPgUUID(userID))
	if err != nil {
		if errors.Is(err, pgx.ErrNoRows) {
			return DefaultPrefs(userID), nil
		}
		return Prefs{}, errs.Wrap(err, errs.CodeInternal, "get notification prefs")
	}
	return Prefs{
		UserID:          fromPgUUID(row.UserID),
		EmailEnabled:    row.EmailEnabled,
		IMEnabled:       row.ImEnabled,
		ImWebhookURLEnc: row.ImWebhookUrlEnc,
		MinSeverity:     Severity(row.MinSeverity),
	}, nil
}

// Upsert 写入（或更新）用户偏好。ImWebhookURLEnc 为 nil 时写 NULL。
func (r *pgPrefsRepo) Upsert(ctx context.Context, p Prefs) error {
	if err := r.q.UpsertNotificationPrefs(ctx, notifysqlc.UpsertNotificationPrefsParams{
		UserID:          toPgUUID(p.UserID),
		EmailEnabled:    p.EmailEnabled,
		ImEnabled:       p.IMEnabled,
		ImWebhookUrlEnc: p.ImWebhookURLEnc,
		MinSeverity:     string(p.MinSeverity),
	}); err != nil {
		return errs.Wrap(err, errs.CodeInternal, "upsert notification prefs")
	}
	return nil
}

// severityRank 把 Severity 映射为可比较的等级序数，用于 min_severity 过滤。
// 未知值按最低（info）处理，确保 "至少投递" 而非误丢。
func severityRank(s Severity) int {
	switch s {
	case SeverityError:
		return 2
	case SeverityWarning:
		return 1
	default:
		return 0
	}
}

// meetsMinSeverity 报告消息级别是否达到（>=）用户设定的最低投递级别。
func meetsMinSeverity(msg Severity, min Severity) bool {
	return severityRank(msg) >= severityRank(min)
}
