package service

import (
	"errors"
	"fmt"
	"regexp"
	"strings"
	"unicode/utf8"

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

	"gorm.io/gorm"
)

// mdPatterns Markdown 语法正则（用于生成纯文本摘要）
var mdPatterns = []*regexp.Regexp{
	// 代码块 ```...```
	regexp.MustCompile("(?s)```[^`]*```"),
	// 行内代码 `...`
	regexp.MustCompile("`[^`]+`"),
	// 图片 ![alt](url)
	regexp.MustCompile(`!\[.*?\]\(.*?\)`),
	// 链接 [text](url)
	regexp.MustCompile(`\[([^\]]*)\]\(.*?\)`),
	// 标题标记 #
	regexp.MustCompile(`^#{1,6}\s+`),
	// 粗体/斜体/删除线标记
	regexp.MustCompile(`\*{1,3}|_{1,3}|~~`),
	// 引用 >
	regexp.MustCompile(`^>\s?`),
	// 列表标记
	regexp.MustCompile(`^[\s]*[-*+]\s+`),
	regexp.MustCompile(`^[\s]*\d+\.\s+`),
	// 分割线
	regexp.MustCompile(`^[-*_]{3,}\s*$`),
}

// generateExcerpt 从 Markdown 生成纯文本摘要（最多 300 字符）
// MD 作为纯文本存储无 XSS 风险，前端由 Vditor 渲染
func generateExcerpt(md string) string {
	text := md

	// 按行处理，保留行结构
	lines := strings.Split(text, "\n")
	var cleaned []string
	for _, line := range lines {
		cleanedLine := line
		for _, re := range mdPatterns {
			if re == mdPatterns[2] {
				// 链接保留文字: [text](url) → text
				cleanedLine = re.ReplaceAllString(cleanedLine, "$1")
			} else {
				cleanedLine = re.ReplaceAllString(cleanedLine, "")
			}
		}
		cleanedLine = strings.TrimSpace(cleanedLine)
		if cleanedLine != "" {
			cleaned = append(cleaned, cleanedLine)
		}
	}

	result := strings.Join(cleaned, " ")

	// 按 rune 截断，不切中文字符
	const maxChars = 300
	if utf8.RuneCountInString(result) > maxChars {
		runes := []rune(result)
		result = string(runes[:maxChars]) + "..."
	}

	return result
}

// PostService 帖子业务逻辑
type PostService struct {
	repo     postStore
	ss       *config.SiteSettings
	notifier postNotifier
}

// NewPostService 构造函数
func NewPostService(repo postStore, ss *config.SiteSettings, notifier postNotifier) *PostService {
	return &PostService{
		repo:     repo,
		ss:       ss,
		notifier: notifier,
	}
}

// auditEnabled 审核是否开启
func (s *PostService) auditEnabled() bool {
	return s.ss.IsAuditEnabled()
}

// findPost 按 ID 查找帖子，区分"未找到"和"数据库错误"
func (s *PostService) findPost(id uint) (*model.Post, error) {
	post, err := s.repo.FindByID(id)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, common.ErrPostNotFound
		}
		return nil, fmt.Errorf("查询帖子失败: %w", err)
	}
	return post, nil
}

// findPostWithAuthor 按 ID 查找帖子（含作者名），区分"未找到"和"数据库错误"
func (s *PostService) findPostWithAuthor(id uint) (*model.Post, error) {
	post, err := s.repo.FindByIDWithAuthor(id)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, common.ErrPostNotFound
		}
		return nil, fmt.Errorf("查询帖子失败: %w", err)
	}
	return post, nil
}

// Create 创建帖子
// body 为 Vditor 输出的 Markdown，后端直接存储不做 HTML 消毒
// MD 是纯文本，无 XSS 注入风险；前端渲染时由 Vditor 转 HTML
func (s *PostService) Create(userID uint, title, body string) (*model.Post, error) {
	status := model.PostStatusApproved
	if s.auditEnabled() {
		status = model.PostStatusDraft
	}

	post := &model.Post{
		Title:        title,
		Body:         body,
		Excerpt:      generateExcerpt(body),
		UserID:       userID,
		Status:       status,
		AllowComment: true,
	}
	if err := s.repo.Create(post); err != nil {
		return nil, err
	}
	return post, nil
}

// GetByID 按 ID 获取帖子（含作者名，仅 approved 公开可见）
func (s *PostService) GetByID(id uint) (*model.Post, error) {
	return s.findPostWithAuthor(id)
}

// listPageable 分页查询公共逻辑：参数规范化 + nil 转空切片
func (s *PostService) listPageable(page, pageSize int, fn func(offset, limit int) ([]model.Post, int64, error)) ([]model.Post, int64, error) {
	p := common.Pagination{Page: page, PageSize: pageSize}
	p.DefaultPagination()
	posts, total, err := fn(p.Offset(), p.PageSize)
	if posts == nil {
		posts = []model.Post{}
	}
	return posts, total, err
}

// List 公开帖子列表（仅 approved）
func (s *PostService) List(keyword string, page, pageSize int) ([]model.Post, int64, error) {
	return s.listPageable(page, pageSize, func(offset, limit int) ([]model.Post, int64, error) {
		return s.repo.FindPageable(keyword, offset, limit)
	})
}

// ListAdmin 管理后台帖子列表（全状态）
func (s *PostService) ListAdmin(keyword, status string, page, pageSize int) ([]model.Post, int64, error) {
	return s.listPageable(page, pageSize, func(offset, limit int) ([]model.Post, int64, error) {
		return s.repo.FindAdminPageable(keyword, status, offset, limit)
	})
}

// ListByUser 查询某用户指定状态的帖子（空 status=全状态）
func (s *PostService) ListByUser(userID uint, status string, page, pageSize int) ([]model.Post, int64, error) {
	return s.listPageable(page, pageSize, func(offset, limit int) ([]model.Post, int64, error) {
		return s.repo.FindByUserIDAndStatus(userID, status, offset, limit)
	})
}

// StudioOverview 创作中心数据概览
type StudioOverview struct {
	TotalPosts   int64 `json:"total_posts"`
	DraftCount   int64 `json:"draft_count"`
	PendingCount int64 `json:"pending_count"`
	ApprovedCount int64 `json:"approved_count"`
	RejectedCount int64 `json:"rejected_count"`
}

// GetOverview 获取创作中心数据概览
func (s *PostService) GetOverview(userID uint) (*StudioOverview, error) {
	// 获取各状态文章数量（分页传 1 条只取 total）
	_, totalPosts, err := s.repo.FindByUserIDAndStatus(userID, "", 0, 1)
	if err != nil {
		return nil, err
	}
	_, draftCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusDraft, 0, 1)
	_, pendingCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusPending, 0, 1)
	_, approvedCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusApproved, 0, 1)
	_, rejectedCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusRejected, 0, 1)

	return &StudioOverview{
		TotalPosts:    totalPosts,
		DraftCount:    draftCount,
		PendingCount:  pendingCount,
		ApprovedCount: approvedCount,
		RejectedCount: rejectedCount,
	}, nil
}

// Update 编辑帖子（权限在 controller 层检查）
func (s *PostService) Update(postID uint, title, body string) error {
	post, err := s.findPost(postID)
	if err != nil {
		return err
	}

	if post.Status == model.PostStatusPending || post.IsLocked {
		return common.ErrPostCannotEdit
	}

	// 已通过帖子：编辑内容保存为待审修订，不影响当前展示
	if post.Status == model.PostStatusApproved {
		post.PendingTitle = title
		post.PendingBody = body
		post.RejectReason = "" // 清空旧退回理由
		return s.repo.Update(post)
	}

	// 草稿/已退回：直接修改原文（现有逻辑）
	post.Title = title
	post.Body = body
	post.Excerpt = generateExcerpt(body)

	if post.Status == model.PostStatusRejected {
		post.Status = model.PostStatusDraft
		post.RejectReason = ""
	}

	return s.repo.Update(post)
}

// Delete 软删除（权限在 controller 层检查）
func (s *PostService) Delete(postID uint) error {
	return s.repo.SoftDelete(postID)
}

// SubmitForAudit 提交审核：draft → pending
func (s *PostService) SubmitForAudit(postID uint) error {
	post, err := s.findPost(postID)
	if err != nil {
		return err
	}
	if post.Status != model.PostStatusDraft && post.Status != model.PostStatusRejected {
		return common.ErrPostCannotSubmit
	}
	post.Status = model.PostStatusPending
	return s.repo.Update(post)
}

// Approve 审核通过：pending → approved；或修订审核通过复制 Pending 到正式字段
func (s *PostService) Approve(postID uint) error {
	post, err := s.findPost(postID)
	if err != nil {
		return err
	}

	wasRevision := post.PendingBody != ""

	// 修订审核：将待审内容覆盖到正式字段
	if wasRevision {
		post.Title = post.PendingTitle
		post.Body = post.PendingBody
		post.Excerpt = generateExcerpt(post.PendingBody)
		post.PendingTitle = ""
		post.PendingBody = ""
		post.Status = model.PostStatusApproved
		post.RejectReason = ""
		if err := s.repo.Update(post); err != nil {
			return err
		}
		// 通知作者修订审核通过
		if s.notifier != nil {
			s.notifier.NotifyPostApproved(post.UserID, post.Title, post.ID)
		}
		return nil
	}

	// 首次审核通过
	if post.Status != model.PostStatusPending {
		return common.ErrPostCannotApprove
	}
	post.Status = model.PostStatusApproved
	post.RejectReason = ""
	if err := s.repo.Update(post); err != nil {
		return err
	}
	// 通知作者审核通过
	if s.notifier != nil {
		s.notifier.NotifyPostApproved(post.UserID, post.Title, post.ID)
	}
	return nil
}

// Reject 退回：pending/approved → rejected；修订退回则清空 Pending 保持 approved
func (s *PostService) Reject(postID uint, reason string) error {
	post, err := s.findPost(postID)
	if err != nil {
		return err
	}
	if post.Status != model.PostStatusPending && post.Status != model.PostStatusApproved {
		return common.ErrPostCannotReject
	}

	// 修订退回：清空待审修订，保持已发布内容不变
	if post.PendingBody != "" {
		post.PendingTitle = ""
		post.PendingBody = ""
		post.RejectReason = reason
		if err := s.repo.Update(post); err != nil {
			return err
		}
		// 通知作者修订被退回
		if s.notifier != nil {
			s.notifier.NotifyPostRejected(post.UserID, post.Title, reason, post.ID)
		}
		return nil
	}

	// 普通退回（首次审核）
	post.Status = model.PostStatusRejected
	post.RejectReason = reason
	if err := s.repo.Update(post); err != nil {
		return err
	}
	// 通知作者审核被退回
	if s.notifier != nil {
		s.notifier.NotifyPostRejected(post.UserID, post.Title, reason, post.ID)
	}
	return nil
}

// Lock 锁定：设置 IsLocked 标志，禁止编辑，不改变帖子发布状态
func (s *PostService) Lock(postID uint) error {
	post, err := s.findPost(postID)
	if err != nil {
		return err
	}
	// 待审核帖子不允许锁定（审核流程中）
	if post.Status == model.PostStatusPending {
		return common.ErrPostCannotLock
	}
	post.IsLocked = true
	return s.repo.Update(post)
}

// Unlock 解锁：清除 IsLocked 标志，恢复可编辑
func (s *PostService) Unlock(postID uint) error {
	post, err := s.findPost(postID)
	if err != nil {
		return err
	}
	if !post.IsLocked {
		return common.ErrPostCannotUnlock
	}
	post.IsLocked = false
	return s.repo.Update(post)
}

// Restore 恢复软删除
func (s *PostService) Restore(postID uint) error {
	err := s.repo.Restore(postID)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return common.ErrPostNotFound
		}
		return err
	}
	return nil
}

// ListPendingRevisions 查询有待审修订的帖子（approved 且 PendingBody 不为空）
func (s *PostService) ListPendingRevisions(keyword string, page, pageSize int) ([]model.Post, int64, error) {
	return s.listPageable(page, pageSize, func(offset, limit int) ([]model.Post, int64, error) {
		return s.repo.FindPendingRevisions(keyword, offset, limit)
	})
}

// IsPostAccessible 检查用户是否有权限访问/操作帖子（作者或 moderator+）
func (s *PostService) IsPostAccessible(userID uint, role string, postUserID uint) bool {
	return userID == postUserID || model.HasMinRole(role, model.RoleModerator)
}
