package category

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

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

type sCategory struct {
}

func New() service.ICategory {
	return &sCategory{}
}

func init() {
	service.RegisterCategory(New())
}

// List retrieves a paginated list of categories
func (s *sCategory) List(ctx context.Context, in *model.CategoryListIn) (out *model.CategoryListOut, err error) {
	out = &model.CategoryListOut{}
	m := dao.Categories.Ctx(ctx)
	if in.Channel != 0 {
		m = m.Where(dao.Categories.Columns().Channel, in.Channel)
	}
	if err = m.Page(in.Page, in.Size).ScanAndCount(&out.List, &out.Total, false); err != nil {
		return
	}
	return
}

func (s *sCategory) Create(ctx context.Context, in *model.CategoryAddIn) (out *model.CategoryCRUDOut, err error) {
	exist, err := dao.Categories.Ctx(ctx).
		Where(dao.Categories.Columns().Name, in.Name).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("category_query_failed")
	}
	if exist {
		return nil, ecode.Params.Sub("category_exists")
	}

	if _, err := dao.Categories.Ctx(ctx).Data(do.Categories{
		Name:    in.Name,
		Channel: in.Channel,
	}).Insert(); err != nil {
		return nil, ecode.Fail.Sub("category_create_failed")
	}

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

func (s *sCategory) Update(ctx context.Context, in *model.CategoryEditIn) (out *model.CategoryCRUDOut, err error) {
	exist, err := dao.Categories.Ctx(ctx).
		WherePri(in.Id).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("category_query_failed")
	}
	if !exist {
		return nil, ecode.NotFound.Sub("category_not_found")
	}

	exist, err = dao.Categories.Ctx(ctx).
		Where(dao.Categories.Columns().Name, in.Name).
		Where("id != ?", in.Id).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("category_query_failed")
	}
	if exist {
		return nil, ecode.Params.Sub("category_exists")
	}

	// Update category
	_, err = dao.Categories.Ctx(ctx).
		WherePri(in.Id).
		Data(do.Categories{
			Name:    in.Name,
			Channel: in.Channel,
		}).Update()
	if err != nil {
		return nil, ecode.Fail.Sub("category_update_failed")
	}

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

func (s *sCategory) Delete(ctx context.Context, in *model.CategoryDelIn) (out *model.CategoryCRUDOut, err error) {
	exist, err := dao.Categories.Ctx(ctx).
		WherePri(in.Id).
		Exist()
	if err != nil {
		return nil, ecode.Fail.Sub("category_query_failed")
	}
	if !exist {
		return nil, ecode.NotFound.Sub("category_not_found")
	}

	// 开启事务，检查是否有书籍使用了这个分类
	err = dao.Categories.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 检查是否有书籍使用了这个分类
		bookCount, err := dao.Books.Ctx(ctx).TX(tx).
			Where(dao.Books.Columns().CategoryId, in.Id).
			Count()
		if err != nil {
			return ecode.Fail.Sub("book_query_failed")
		}

		if bookCount > 0 {
			return ecode.Fail.Sub("category_in_use")
		}

		// 删除分类
		_, err = dao.Categories.Ctx(ctx).TX(tx).WherePri(in.Id).Delete()
		if err != nil {
			return ecode.Fail.Sub("category_delete_failed")
		}

		return nil
	})

	if err != nil {
		return nil, err
	}

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