package service

import (
	"encoding/json"
	"fmt"
	"sort"

	"metazone.cc/metalab/internal/common"
	"metazone.cc/metalab/internal/model"
)

// ParseNotifyPrefs 解析用户通知偏好 JSON 字符串
func ParseNotifyPrefs(raw string) map[string]bool {
	prefs := map[string]bool{
		model.NotifyComment:        true,
		model.NotifyCommentReply:   true,
		model.NotifyLikeAggregated: true,
		model.NotifyFollow:         true,
	}
	if raw == "" {
		return prefs
	}
	var parsed map[string]bool
	if err := json.Unmarshal([]byte(raw), &parsed); err != nil {
		return prefs
	}
	for k, v := range parsed {
		prefs[k] = v
	}
	return prefs
}

// notifRepo 通知服务所需的最小仓储接口（ISP）
type notifRepo interface {
	Create(n *model.Notification) error
	ListByUser(userID uint, offset, limit int) ([]model.Notification, error)
	ListByUserAndType(userID uint, notifyType string, offset, limit int) ([]model.Notification, error)
	ListByUserExcludeTypes(userID uint, excludeTypes []string, offset, limit int) ([]model.Notification, error)
	CountByUser(userID uint) (int64, error)
	CountByUserExcludeTypes(userID uint, excludeTypes []string) (int64, error)
	CountByUserAndType(userID uint, notifyType string) (int64, error)
	CountUnread(userID uint) (int64, error)
	CountUnreadExcludeTypes(userID uint, excludeTypes []string) (int64, error)
	CountUnreadByType(userID uint, notifyType string) (int64, error)
	MarkRead(id, userID uint) error
	MarkAllRead(userID uint) error
	GetUnnotifiedDailyLikeSummaries(userID uint) ([]model.DailyLikeSummary, error)
	MarkDailyLikeSummaryNotified(id uint) error
	GetUsernameByID(userID uint) (string, error)
	GetNotifyPrefs(userID uint) (string, error)
}

// NotificationService 消息/通知业务逻辑
type NotificationService struct {
	repo notifRepo
}

// NewNotificationService 构造函数
func NewNotificationService(repo notifRepo) *NotificationService {
	return &NotificationService{repo: repo}
}

// Create 创建消息
func (s *NotificationService) Create(userID uint, notifyType, title, content string, relatedID, commentID *uint) error {
	n := &model.Notification{
		UserID:     userID,
		NotifyType: notifyType,
		Title:      title,
		Content:    content,
		IsRead:     false,
		RelatedID:  relatedID,
		CommentID:  commentID,
	}
	return s.repo.Create(n)
}

// List 分页查询用户消息列表（触发聚合通知检查）
func (s *NotificationService) List(userID uint, page, pageSize int) (*model.NotificationListResult, error) {
	// 自动触发点赞聚合通知生成
	s.checkAndNotifyAggregation(userID)

	p := common.Pagination{Page: page, PageSize: pageSize}
	p.DefaultPagination()

	excludeTypes := s.getDisabledTypes(userID)

	total, err := s.repo.CountByUserExcludeTypes(userID, excludeTypes)
	if err != nil {
		return nil, err
	}

	items, err := s.repo.ListByUserExcludeTypes(userID, excludeTypes, p.Offset(), p.PageSize)
	if err != nil {
		return nil, err
	}
	if items == nil {
		items = []model.Notification{}
	}

	totalPages := int((total + int64(p.PageSize) - 1) / int64(p.PageSize))
	unread, _ := s.CountUnread(userID)

	return &model.NotificationListResult{
		Items:      items,
		Total:      total,
		Page:       p.Page,
		TotalPages: totalPages,
		Unread:     unread,
	}, nil
}

// ListByCategory 按通知分类分页查询（供消息页分类 TAB 使用）
func (s *NotificationService) ListByCategory(userID uint, category string, page, pageSize int) (*model.NotificationListResult, error) {
	s.checkAndNotifyAggregation(userID)

	p := common.Pagination{Page: page, PageSize: pageSize}
	p.DefaultPagination()

	// 分类→通知类型映射
	notifyTypes := categoryTypes(category)
	var combinedItems []model.Notification
	var total int64

	for _, nt := range notifyTypes {
		c, _ := s.repo.CountByUserAndType(userID, nt)
		total += c
	}

	allItems := make([]model.Notification, 0)
	for _, nt := range notifyTypes {
		items, err := s.repo.ListByUserAndType(userID, nt, 0, 10000) // 大 limit，在内存里分页
		if err != nil {
			continue
		}
		allItems = append(allItems, items...)
	}

	// 按 created_at 降序排序
	sortNotifications(allItems)

	// 分页
	start := p.Offset()
	end := start + p.PageSize
	if start > len(allItems) {
		start = len(allItems)
	}
	if end > len(allItems) {
		end = len(allItems)
	}
	if start < end {
		combinedItems = allItems[start:end]
	}

	if combinedItems == nil {
		combinedItems = []model.Notification{}
	}

	totalPages := int((total + int64(p.PageSize) - 1) / int64(p.PageSize))
	unread, _ := s.CountUnread(userID)

	return &model.NotificationListResult{
		Items:      combinedItems,
		Total:      total,
		Page:       p.Page,
		TotalPages: totalPages,
		Unread:     unread,
	}, nil
}

// checkAndNotifyAggregation 检查并生成点赞聚合通知
func (s *NotificationService) checkAndNotifyAggregation(userID uint) {
	summaries, err := s.repo.GetUnnotifiedDailyLikeSummaries(userID)
	if err != nil || len(summaries) == 0 {
		return
	}

	for _, sm := range summaries {
		count := countUIDs(sm.LikerUIDs)
		var content string
		if count <= 3 {
			content = fmt.Sprintf("你的文章被 %d 人点赞了", count)
		} else {
			content = fmt.Sprintf("你的文章被 %d 人点赞了", count)
		}
		_ = s.Create(userID, model.NotifyLikeAggregated,
			"点赞汇总通知",
			content,
			nil, nil)
		_ = s.repo.MarkDailyLikeSummaryNotified(sm.ID)
	}
}

func countUIDs(uids string) int {
	if uids == "" {
		return 0
	}
	count := 1
	for _, c := range uids {
		if c == ',' {
			count++
		}
	}
	return count
}

// categoryTypes 分类→通知类型列表
func categoryTypes(category string) []string {
	switch category {
	case "system":
		return []string{model.NotifyAuditApproved, model.NotifyAuditRejected, model.NotifyPostApproved, model.NotifyPostRejected, model.NotifyLevelUp}
	case "mention":
		return []string{model.NotifyComment, model.NotifyCommentReply}
	case "like":
		return []string{model.NotifyLikeAggregated}
	case "follow":
		return []string{model.NotifyFollow}
	default:
		return []string{}
	}
}

// sortNotifications 按 CreatedAt 降序排序
func sortNotifications(items []model.Notification) {
	sort.Slice(items, func(i, j int) bool {
		return items[i].CreatedAt.After(items[j].CreatedAt)
	})
}

// CountUnread 统计未读消息数（排除用户禁用的通知类型）
func (s *NotificationService) CountUnread(userID uint) (int64, error) {
	excludeTypes := s.getDisabledTypes(userID)
	return s.repo.CountUnreadExcludeTypes(userID, excludeTypes)
}

// getDisabledTypes 获取用户禁用的通知类型列表
func (s *NotificationService) getDisabledTypes(userID uint) []string {
	raw, err := s.repo.GetNotifyPrefs(userID)
	if err != nil || raw == "" {
		return nil
	}
	prefs := ParseNotifyPrefs(raw)
	var disabled []string
	for _, t := range []string{
		model.NotifyComment,
		model.NotifyCommentReply,
		model.NotifyLikeAggregated,
		model.NotifyFollow,
	} {
		if !prefs[t] {
			disabled = append(disabled, t)
		}
	}
	return disabled
}

// MarkRead 标记单条消息为已读
func (s *NotificationService) MarkRead(id, userID uint) error {
	return s.repo.MarkRead(id, userID)
}

// MarkAllRead 标记所有未读为已读
func (s *NotificationService) MarkAllRead(userID uint) error {
	return s.repo.MarkAllRead(userID)
}

// NotifyAuditApproved 审核通过通知
func (s *NotificationService) NotifyAuditApproved(userID uint, auditType string, relatedID uint) {
	typeName := model.AuditTypeNames[auditType]
	if typeName == "" {
		typeName = auditType
	}
	_ = s.Create(userID, model.NotifyAuditApproved,
		"审核已通过",
		"你的"+typeName+"修改已通过审核",
		&relatedID, nil)
}

// NotifyAuditRejected 审核拒绝通知
func (s *NotificationService) NotifyAuditRejected(userID uint, auditType, reason string, relatedID uint) {
	typeName := model.AuditTypeNames[auditType]
	if typeName == "" {
		typeName = auditType
	}
	content := "你的" + typeName + "修改被拒绝"
	if reason != "" {
		content += "，理由：" + reason
	}
	_ = s.Create(userID, model.NotifyAuditRejected,
		"审核未通过",
		content,
		&relatedID, nil)
}

// NotifyPostApproved 稿件审核通过通知
func (s *NotificationService) NotifyPostApproved(userID uint, postTitle string, postID uint) {
	title := "稿件审核通过"
	content := "你的稿件《" + postTitle + "》已通过审核，现已公开发布"
	_ = s.Create(userID, model.NotifyPostApproved, title, content, &postID, nil)
}

// NotifyPostRejected 稿件审核拒绝通知
func (s *NotificationService) NotifyPostRejected(userID uint, postTitle, reason string, postID uint) {
	title := "稿件审核未通过"
	content := "你的稿件《" + postTitle + "》未通过审核"
	if reason != "" {
		content += "，理由：" + reason
	}
	_ = s.Create(userID, model.NotifyPostRejected, title, content, &postID, nil)
}

// NotifyFollow 关注通知（followerID 关注了 followeeID）
func (s *NotificationService) NotifyFollow(followerID, followeeID uint) {
	// 获取关注者用户名
	followerName := s.getUsername(followerID)
	if followerName == "" {
		followerName = "一位用户"
	}
	title := "关注通知"
	content := followerName + " 关注了你"
	relID := followerID
	_ = s.Create(followeeID, model.NotifyFollow, title, content, &relID, nil)
}

// getUsername 获取用户名（辅助方法）
func (s *NotificationService) getUsername(userID uint) string {
	name, err := s.repo.GetUsernameByID(userID)
	if err != nil || name == "" {
		return "一位用户"
	}
	return name
}
