// Package models 提供CMS数据模型和数据库操作
// 包含文章、分类、标签、评论等实体的数据结构和CRUD操作
package models

import (
	"fmt"

	"bsm/full/module/base/cms/internal/impl"
	pb "bsm/full/module/base/cms/pb"
	"git.apinb.com/bsm-sdk/core/errcode"
	"git.apinb.com/bsm-sdk/core/types"
	"git.apinb.com/bsm-sdk/core/utils"
	"gorm.io/gorm"
)

// 数据库表名常量
const (
	postTable               = "cms_post"      // 文章表
	accessoryTable          = "cms_accessory" // 附件表
	postRelateCategoryTable = "cms_relate"    // 关联表
)

// InitData 初始化基础数据
// 创建系统必需的默认数据，如根分类等
func InitData() {
	var cnt int64 = 0

	// 检查是否存在根分类，如果不存在则创建
	err := impl.DBService.Model(&CmsCategory{}).Where("identity=?", "_RootCategory").Count(&cnt).Error
	if cnt == 0 || err != nil {
		data := &CmsCategory{
			Title:        "根目录",
			ParentId:     0, // 0表示顶级分类
			Std_Identity: types.Std_Identity{Identity: "_RootCategory"},
		}
		impl.DBService.Create(data)
	}
}

// AddPost 添加文章及其关联数据
// 使用事务确保数据一致性，同时创建文章、分类关联、标签关联和附件信息
// 参数:
//   - post: 文章数据模型
//   - accessory: 附件数据列表
//   - category: 分类标识列表
//   - tags: 标签标识列表
//
// 返回:
//   - error: 错误信息
func AddPost(post *CmsPost, accessory []*pb.AccessoryItem, category, tags []string) (err error) {
	var (
		accessoryPath = make([]CmsAccessory, 0)      // 文章附件数据
		categoryData  = make([]CmsRelateCategory, 0) // 文章分类关联数据
		TagsData      = make([]CmsRelateTags, 0)     // 文章标签关联数据
	)

	// 构建分类关联数据
	for _, va := range category {
		categoryData = append(categoryData, CmsRelateCategory{
			Identity:         utils.UUID(),
			PostIdentity:     post.Identity,
			CategoryIdentity: va,
		})
	}

	// 构建标签关联数据
	for _, val := range tags {
		TagsData = append(TagsData, CmsRelateTags{
			Identity:     utils.ULID(),
			PostIdentity: post.Identity,
			TagsIdentity: val,
		})
	}

	// 使用事务确保数据一致性
	return impl.DBService.Transaction(func(tx *gorm.DB) error {
		// 创建文章记录
		if err = tx.Create(post).Error; err != nil {
			return err
		}

		// 创建附件关联记录
		if len(accessory) != 0 {
			for _, v := range accessory {
				accessoryPath = append(accessoryPath, CmsAccessory{
					Std_Identity: types.Std_Identity{Identity: utils.UUID()},
					PostId:       post.ID,
					PostIdentity: post.Identity,
					FilePath:     v.FilePath,
					Title:        v.Title,
				})
			}
			if err := tx.Create(&accessoryPath).Error; err != nil {
				return err
			}
		}

		// 创建分类关联记录
		if len(category) != 0 {
			if err := tx.Create(&categoryData).Error; err != nil {
				return err
			}
		}

		// 创建标签关联记录
		if len(tags) != 0 {
			if err := tx.Create(&TagsData).Error; err != nil {
				return err
			}
		}

		return nil
	})
}

// PostList 文章列表
func PostList(page, size int64, categoryIdentity, keyword string, userType int64) (list []*CmsPost, cnt int64, err error) {

	tx := impl.DBService.Debug().Model(&CmsPost{})
	if categoryIdentity != "" {
		tx = tx.Joins("left join cms_relate_category crc on crc.post_identity = cms_post.identity ")
		tx = tx.Where("crc.category_identity = ?", categoryIdentity)
	}

	if keyword != "" {
		tx = tx.Where("cms_post.title like ?", "%"+keyword+"%")
	}

	if userType != 0 {
		tx = tx.Where("cms_post.types = ?", userType)
	}

	if err = tx.Count(&cnt).Order("cms_post.created_at desc").Limit(int(size)).Offset(int((page - 1) * size)).Error; err != nil {
		return nil, 0, errcode.ErrDB
	}
	err = tx.Preload("Accessories").Preload("Categories.Category").Preload("Tags.Tags").Find(&list).Error

	return
}

// ModifyPost 修改文章
func ModifyPost(identity string, post *CmsPost, accessory, category, tags []string) (err error) {
	var (
		accessoryPath = make([]CmsAccessory, 0)
		categoryData  = make([]CmsRelateCategory, 0)
		tagsData      = make([]CmsRelateTags, 0)
	)
	for _, v := range category {
		categoryData = append(categoryData, CmsRelateCategory{
			Identity:         utils.UUID(),
			PostIdentity:     identity,
			CategoryIdentity: v,
		})
	}
	for _, val := range tags {
		tagsData = append(tagsData, CmsRelateTags{
			Identity:     utils.ULID(),
			PostIdentity: identity,
			TagsIdentity: val,
		})
	}
	for _, v := range accessory {
		accessoryPath = append(accessoryPath, CmsAccessory{
			Std_Identity: types.Std_Identity{Identity: utils.UUID()},
			PostId:       post.ID,
			PostIdentity: identity,
			FilePath:     v,
		})
	}
	return impl.DBService.Transaction(func(tx *gorm.DB) error {
		if err = tx.Where("identity = ?", identity).Updates(post).Error; err != nil {
			return err
		}
		// 删除旧的附件、分类和标签关联
		if err := tx.Where("post_identity = ?", identity).Delete(&CmsAccessory{}).Error; err != nil {
			return err
		}
		if err := tx.Where("post_identity = ?", identity).Delete(&CmsRelateCategory{}).Error; err != nil {
			return err
		}
		if err := tx.Where("post_identity = ?", identity).Delete(&CmsRelateTags{}).Error; err != nil {
			return err
		}
		// 创建新的分类关联
		if len(category) != 0 {
			if err := tx.Create(&categoryData).Error; err != nil {
				return err
			}
		}
		// 创建新的标签关联
		if len(tags) != 0 {
			if err := tx.Create(&tagsData).Error; err != nil {
				return err
			}
		}
		// 创建新的附件关联
		if len(accessory) != 0 {
			if err := tx.Create(&accessoryPath).Error; err != nil {
				return err
			}
		}
		return nil
	})
}

// DeletePost 删除文章
func DeletePost(identity, authorIdentity string) (err error) {
	return impl.DBService.Transaction(func(tx *gorm.DB) error {
		if err = tx.Where("identity = ?", identity).Delete(&CmsPost{}).Error; err != nil {
			return err
		}

		if err = tx.Where("post_identity = ?", identity).Delete(&CmsRelateCategory{}).Error; err != nil {
			return err
		}

		if err = tx.Where("post_identity = ?", identity).Delete(&CmsRelateTags{}).Error; err != nil {
			return err
		}
		if err = tx.Where("post_identity = ?", identity).Delete(&CmsAccessory{}).Error; err != nil {
			return err
		}
		return nil
	})
}

// GetPost 根据指定字段获取文章详情
// 查询成功后会更新文章的点击量
// 参数:
//   - key: 查询字段名（如"identity"、"key"等）
//   - val: 查询字段值
//
// 返回:
//   - *CmsPost: 文章详情
//   - error: 错误信息
func GetPost(key, val string) (*CmsPost, error) {
	var post = &CmsPost{}

	// 使用事务确保查询和更新操作的原子性
	return post, impl.DBService.Transaction(func(tx *gorm.DB) error {
		// 查询文章详情，预加载关联数据
		if err := tx.Model(&CmsPost{}).Preload("Accessories").Preload("Categories").Preload("Tags").
			Where(key+" = ?", val).First(post).Error; err != nil {
			return err
		}

		// 更新文章点击量，只有在查询成功后才更新
		if err := tx.Model(&CmsPost{}).Where(key+" = ?", val).
			UpdateColumn("hits", gorm.Expr("hits + 1")).Error; err != nil {
			return err
		}

		return nil
	})
}

// IncrOrDescPostField 增加或减少文章字段值
// 用于更新文章的点赞、踩赞、评论等计数字段
// 参数:
//   - identity: 文章唯一标识
//   - column: 要更新的字段名
//   - desc: true表示减少，false表示增加
//
// 返回:
//   - error: 错误信息
func IncrOrDescPostField(identity, column string, desc bool) (err error) {
	expr := "%s + ?"
	if desc {
		expr = "%s - ?"
	}
	if err := impl.DBService.Model(&CmsPost{}).Where("identity = ?", identity).UpdateColumn(column, gorm.Expr(fmt.Sprintf(expr, column), 1)).Error; err != nil {
		return err
	}
	return nil
}

func AddComment(comment *CmsComment, authorIdentity string) (err error) {
	err = impl.DBService.Transaction(func(tx *gorm.DB) error {
		if err := tx.Create(comment).Error; err != nil {
			return err
		}
		// 添加评论,更新文章评论量
		err := tx.Model(&CmsPost{}).Where("identity = ?", comment.PostIdentity).UpdateColumn("comment_hits", gorm.Expr("comment_hits + ?", 1)).Error
		if err != nil {
			return err
		}
		if comment.ParentId != 0 {
			// 更新被评论的那条评论的计数
			err = tx.Model(&CmsComment{}).Where("id = ?", comment.ParentId).UpdateColumn("comment_hits", gorm.Expr("comment_hits + ?", 1)).Error
			if err != nil {
				return err
			}
		}
		return nil
	})
	return
}

func ModifyComment(comment *CmsComment) (err error) {
	return impl.DBService.Where("identity = ?", comment.Identity).Updates(comment).Error
}

func DeleteComment(identity string) (err error) {
	// get comment
	comment := new(CmsComment)
	err = impl.DBService.Where("identity = ?", identity).First(comment).Error
	if err != nil {
		return err
	}
	err = impl.DBService.Transaction(func(tx *gorm.DB) error {
		// 删除评论,更新文章评论量
		if err := tx.Where("identity = ?", identity).Delete(&CmsComment{}).Error; err != nil {
			return err
		}
		if err := tx.Model(&CmsPost{}).Where("identity = ?", comment.PostIdentity).
			UpdateColumn("comment_hits", gorm.Expr("comment_hits - ?", 1)).Error; err != nil {
			return err
		}
		if comment.ParentId != 0 {
			// 更新被评论的那条评论的计数
			if err := tx.Model(&CmsComment{}).Where("id = ?", comment.ParentId).
				UpdateColumn("comment_hits", gorm.Expr("comment_hits - ?", 1)).Error; err != nil {
				return err
			}
		}
		// 删除子级评论
		err = tx.Where("parent_id = ?", comment.ID).Delete(&CmsComment{}).Error
		if err != nil {
			return err
		}
		return nil
	})
	return
}

// IncrOrDescCommentField 增加或减少评论字段值
// 用于更新评论的点赞、踩赞等计数字段
// 参数:
//   - identity: 评论唯一标识
//   - column: 要更新的字段名
//   - desc: true表示减少，false表示增加
//
// 返回:
//   - error: 错误信息
//
// TODO: 记录点赞对象避免重复点赞
func IncrOrDescCommentField(identity, column string, desc bool) (err error) {
	expr := "%s + ?"
	if desc {
		expr = "%s - ?"
	}
	if err = impl.DBService.Model(&CmsComment{}).Where("identity = ?", identity).UpdateColumn(column, gorm.Expr(fmt.Sprintf(expr, column), 1)).Error; err != nil {
		return err
	}
	return nil
}

// CategoryList 文章分类列表
func CategoryList() (list []CmsCategory, cnt int64, err error) {
	result := impl.DBService.Preload("Children").Find(&list)
	if result.Error != nil {
		err = result.Error
		return
	}
	cnt = result.RowsAffected
	return
}

// DeleteCategory 删除分类
func DeleteCategory(identity string) (err error) {
	var (
		cnt      int64 = 0
		category CmsCategory
	)
	err = impl.DBService.Model(&CmsCategory{}).Where("identity = ?", identity).Find(&category).Error
	if err != nil {
		return err
	}
	impl.DBService.Model(&CmsCategory{}).Where("parent_id = ?", category.ID).Count(&cnt)
	if cnt != 0 {
		return errcode.NewError(500, "ErrCategoryHasSubcategories")
	}

	err = impl.DBService.Where("identity = ?", identity).Delete(&CmsCategory{}).Error
	if err != nil {
		return err
	}
	return
}
