package author

import (
	"context"
	"server/internal/dao"
	"server/internal/model"
	"server/internal/model/do"
	"server/internal/service"
	"server/utility/ecode"
	"server/utility/encrypt"

	"github.com/gogf/gf/v2/database/gdb"
)

type sAuthor struct{}

func New() service.IAuthor {
	return &sAuthor{}
}
func init() {
	service.RegisterAuthor(New())
}

// List retrieves a paginated list of authors
func (s *sAuthor) List(ctx context.Context, in *model.AuthorListIn) (out *model.AuthorListOut, err error) {
	out = &model.AuthorListOut{}
	m := dao.Authors.Ctx(ctx)
	if in.PenName != "" {
		m = m.Where(dao.Authors.Columns().PenName+" like ?", "%"+in.PenName+"%")
	}
	if in.Status != 0 {
		m = m.Where(dao.Authors.Columns().Status, in.Status)
	}
	if err = m.Page(in.Page, in.Size).WithAll().ScanAndCount(&out.List, &out.Total, false); err != nil {
		return
	}
	return
}

// Create adds a new author
func (s *sAuthor) Create(ctx context.Context, in *model.AuthorAddIn) (out *model.AuthorCRUDOut, err error) {
	// 开启事务，确保用户和作者同时写入
	err = dao.Authors.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 检查该 userId 是否已存在作者
		exist, err := dao.Authors.Ctx(ctx).TX(tx).
			Where(dao.Authors.Columns().UserId, in.UserId).
			Exist()
		if err != nil {
			return ecode.Fail.Sub("author_query_failed")
		}
		if exist {
			return ecode.Params.Sub("author_user_exists")
		}

		// 检查用户是否存在
		userExist, err := dao.Users.Ctx(ctx).TX(tx).
			Where(dao.Users.Columns().Id, in.UserId).
			Exist()
		if err != nil {
			return ecode.Fail.Sub("author_query_failed")
		}
		if !userExist {
			// 不存在则创建用户，用户名用笔名，密码默认 Aa123456
			hash, err := encrypt.EncryptPassword("Aa123456")
			if err != nil {
				return ecode.Fail.Sub("password_encryption_failed")
			}
			result, err := dao.Users.Ctx(ctx).TX(tx).Data(do.Users{
				Username:     in.PenName,
				PasswordHash: hash,
			}).InsertAndGetId()
			if err != nil {
				return ecode.Fail.Sub("author_create_failed")
			}
			in.UserId = result
		}

		// 创建作者
		_, err = dao.Authors.Ctx(ctx).TX(tx).Data(do.Authors{
			UserId:  in.UserId,
			PenName: in.PenName,
			Bio:     in.Bio,
			Status:  in.Status,
		}).Insert()
		if err != nil {
			return ecode.Fail.Sub("author_create_failed")
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &model.AuthorCRUDOut{Success: true}, nil
}

// Update edits an author
func (s *sAuthor) Update(ctx context.Context, in *model.AuthorEditIn) (out *model.AuthorCRUDOut, err error) {
	exist, err := dao.Authors.Ctx(ctx).
		WherePri(in.Id).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}
	if !exist {
		return nil, ecode.NotFound.Sub("author_not_found")
	}
	_, err = dao.Authors.Ctx(ctx).
		WherePri(in.Id).
		Data(do.Authors{
			PenName: in.PenName,
			Bio:     in.Bio,
			Status:  in.Status,
		}).Update()
	if err != nil {
		return nil, ecode.Fail.Sub("author_update_failed")
	}
	return &model.AuthorCRUDOut{Success: true}, nil
}

// Delete removes an author by id
func (s *sAuthor) Delete(ctx context.Context, in *model.AuthorDelIn) (out *model.AuthorCRUDOut, err error) {
	exist, err := dao.Authors.Ctx(ctx).
		WherePri(in.Id).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}
	if !exist {
		return nil, ecode.NotFound.Sub("author_not_found")
	}

	// 开启事务，删除作者及相关数据
	err = dao.Authors.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 1. 删除作者相关的用户关注记录
		_, err := dao.UserFollowAuthors.Ctx(ctx).TX(tx).
			Where(dao.UserFollowAuthors.Columns().AuthorId, in.Id).
			Delete()
		if err != nil {
			return ecode.Fail.Sub("follow_author_delete_failed")
		}

		// 2. 删除作者相关的书籍（这里需要递归删除书籍相关的所有数据）
		// 先查询该作者的所有书籍ID
		var bookIds []int64
		err = dao.Books.Ctx(ctx).TX(tx).
			Where(dao.Books.Columns().AuthorId, in.Id).
			Fields("id").
			Scan(&bookIds)
		if err != nil {
			return ecode.Fail.Sub("book_query_failed")
		}

		if len(bookIds) > 0 {
			// 删除书籍相关的章节
			_, err = dao.Chapters.Ctx(ctx).TX(tx).
				WhereIn(dao.Chapters.Columns().BookId, bookIds).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("chapter_delete_failed")
			}

			// 删除书籍相关的用户阅读记录
			_, err = dao.UserReadRecords.Ctx(ctx).TX(tx).
				WhereIn(dao.UserReadRecords.Columns().BookId, bookIds).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("read_record_delete_failed")
			}

			// 删除书籍相关的用户阅读历史
			_, err = dao.UserReadHistory.Ctx(ctx).TX(tx).
				WhereIn(dao.UserReadHistory.Columns().BookId, bookIds).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("history_delete_failed")
			}

			// 删除书籍相关的用户书架
			_, err = dao.Bookshelves.Ctx(ctx).TX(tx).
				WhereIn(dao.Bookshelves.Columns().BookId, bookIds).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("bookshelf_delete_failed")
			}

			// 删除书籍相关的用户评分
			_, err = dao.BookRatings.Ctx(ctx).TX(tx).
				WhereIn(dao.BookRatings.Columns().BookId, bookIds).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("rating_delete_failed")
			}

			// 删除书籍相关的章节购买记录
			_, err = dao.UserChapterPurchases.Ctx(ctx).TX(tx).
				WhereIn(dao.UserChapterPurchases.Columns().BookId, bookIds).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("purchase_delete_failed")
			}

			// 最后删除书籍
			_, err = dao.Books.Ctx(ctx).TX(tx).
				Where(dao.Books.Columns().AuthorId, in.Id).
				Delete()
			if err != nil {
				return ecode.Fail.Sub("book_delete_failed")
			}
		}

		// 3. 最后删除作者
		_, err = dao.Authors.Ctx(ctx).TX(tx).WherePri(in.Id).Delete()
		if err != nil {
			return ecode.Fail.Sub("author_delete_failed")
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

	return &model.AuthorCRUDOut{Success: true}, nil
}

// Apply 允许用户申请成为作者
func (s *sAuthor) Apply(ctx context.Context, in *model.AuthorApplyIn) (out *model.AuthorApplyOut, err error) {
	exist, err := dao.Authors.Ctx(ctx).
		Where(dao.Authors.Columns().UserId, in.UserId).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}
	if exist {
		return nil, ecode.Params.Sub("author_user_exists")
	}
	if _, err := dao.Authors.Ctx(ctx).Data(do.Authors{
		UserId:  in.UserId,
		PenName: in.PenName,
		Bio:     in.Bio,
		Status:  2, // 默认禁用
	}).Insert(); err != nil {
		return nil, ecode.Fail.Sub("author_create_failed")
	}
	return &model.AuthorApplyOut{Success: true}, nil
}
func (s *sAuthor) Detail(ctx context.Context, in *model.AuthorDetailIn) (out *model.AuthorDetailOut, err error) {
	out = &model.AuthorDetailOut{}
	err = dao.Authors.Ctx(ctx).WherePri(in.AuthorId).WithAll().Scan(&out)
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}
	if out == nil {
		return nil, ecode.NotFound.Sub("author_not_found")
	}
	userId := ctx.Value("id")
	if userId != nil {
		exist, err := dao.UserFollowAuthors.Ctx(ctx).
			Where(dao.UserFollowAuthors.Columns().UserId, userId).
			Where(dao.UserFollowAuthors.Columns().AuthorId, in.AuthorId).
			Exist()
		if err != nil {
			return nil, ecode.Fail.Sub("user_follow_author_query_failed")
		}
		out.IsFollowed = exist
	}
	// 查询作者作品数量
	out.WorksCount, err = dao.Books.Ctx(ctx).
		Where(dao.Books.Columns().AuthorId, in.AuthorId).
		Count()
	if err != nil {
		return nil, ecode.Fail.Sub("author_book_count_failed")
	}
	return out, nil
}

// AuthorInfo 获取作者信息
func (s *sAuthor) AuthorInfo(ctx context.Context, in *model.AuthorInfoIn) (out *model.AuthorInfoOut, err error) {
	exist, err := dao.Authors.Ctx(ctx).Where(dao.Authors.Columns().UserId, in.UserId).Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}
	if !exist {
		return nil, ecode.NotFound.Sub("author_not_found")
	}

	var author struct {
		Id      int64  `json:"id"`
		PenName string `json:"penName"`
	}
	err = dao.Authors.Ctx(ctx).Where(dao.Authors.Columns().UserId, in.UserId).Fields("id, pen_name").Scan(&author)
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}

	return &model.AuthorInfoOut{
		Id:      author.Id,
		PenName: author.PenName,
		Role:    "author",
	}, nil
}

// 审核作者申请（通过/拒绝）
func (s *sAuthor) Review(ctx context.Context, in *model.AuthorReviewIn) (out *model.AuthorReviewOut, err error) {
	exist, err := dao.Authors.Ctx(ctx).WherePri(in.AuthorId).Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("author_query_failed")
	}
	if !exist {
		return nil, ecode.NotFound.Sub("author_not_found")
	}
	_, err = dao.Authors.Ctx(ctx).WherePri(in.AuthorId).Data(do.Authors{
		Status: in.Status,
	}).Update()
	if err != nil {
		return nil, ecode.Fail.Sub("author_review_failed")
	}
	return &model.AuthorReviewOut{Success: true}, nil
}
