package biz

import (
	"context"

	pb "moredoc/api/v1"
	"moredoc/middleware/auth"
	"moredoc/model"
	"moredoc/util"

	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"gorm.io/gorm"
)

type ArticleAPIService struct {
	pb.UnimplementedArticleAPIServer
	dbModel *model.DBModel
	logger  *zap.Logger
}

func NewArticleAPIService(dbModel *model.DBModel, logger *zap.Logger) (service *ArticleAPIService) {
	return &ArticleAPIService{dbModel: dbModel, logger: logger.Named("ArticleAPIService")}
}

func (s *ArticleAPIService) checkPermission(ctx context.Context) (userClaims *auth.UserClaims, err error) {
	return checkGRPCPermission(s.dbModel, ctx)
}

// CreateArticle 创建文章
func (s *ArticleAPIService) CreateArticle(ctx context.Context, req *pb.Article) (*emptypb.Empty, error) {
	_, err := s.checkPermission(ctx)
	if err != nil {
		return nil, err
	}

	existArticle, _ := s.dbModel.GetArticleByIdentifier(req.Identifier, "id")
	if existArticle.Id > 0 {
		return nil, status.Errorf(codes.AlreadyExists, "文章标识符已存在")
	}

	article := &model.Article{}
	err = util.CopyStruct(req, article)
	if err != nil {
		s.logger.Error("CreateArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "创建文章失败")
	}

	err = s.dbModel.CreateArticle(article)
	if err != nil {
		s.logger.Error("CreateArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "创建文章失败")
	}

	return &emptypb.Empty{}, nil
}

// UpdateArticle 更新文章。注意：不支持更新identifier
func (s *ArticleAPIService) UpdateArticle(ctx context.Context, req *pb.Article) (*emptypb.Empty, error) {
	_, err := s.checkPermission(ctx)
	if err != nil {
		return nil, err
	}

	article := &model.Article{}
	err = util.CopyStruct(req, article)
	if err != nil {
		s.logger.Error("UpdateArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "更新文章失败")
	}

	err = s.dbModel.UpdateArticle(article)
	if err != nil {
		s.logger.Error("UpdateArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "更新文章失败")
	}

	return &emptypb.Empty{}, nil
}

// DeleteArticle 删除文章
func (s *ArticleAPIService) DeleteArticle(ctx context.Context, req *pb.DeleteArticleRequest) (*emptypb.Empty, error) {
	_, err := s.checkPermission(ctx)
	if err != nil {
		return nil, err
	}

	err = s.dbModel.DeleteArticle(req.Id)
	if err != nil {
		s.logger.Error("DeleteArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "删除文章失败")
	}

	return &emptypb.Empty{}, nil
}

// GetArticle 根据id或文章标识获取文章
// 不需要鉴权
func (s *ArticleAPIService) GetArticle(ctx context.Context, req *pb.GetArticleRequest) (*pb.Article, error) {
	var (
		article model.Article
		err     error
	)
	if req.Id > 0 {
		article, err = s.dbModel.GetArticle(req.Id)
		if err != nil && err != gorm.ErrRecordNotFound {
			s.logger.Error("GetArticle", zap.Error(err))
			return nil, status.Errorf(codes.Internal, "获取文章失败")
		}
	} else {
		article, err = s.dbModel.GetArticleByIdentifier(req.Identifier)
		if err != nil && err != gorm.ErrRecordNotFound {
			s.logger.Error("GetArticle", zap.Error(err))
			return nil, status.Errorf(codes.Internal, "获取文章失败")
		}
		article.ViewCount += 1
		s.dbModel.UpdateArticleViewCount(article.Id, article.ViewCount)
	}

	if article.Id == 0 {
		return nil, status.Errorf(codes.NotFound, "文章不存在")
	}

	pbArticle := &pb.Article{}
	util.CopyStruct(article, pbArticle)
	return pbArticle, nil
}

// ListArticles 获取文章列表。
// 如果是有权限，则可以根据关键字来查询，否则只能简单查询
func (s *ArticleAPIService) ListArticle(ctx context.Context, req *pb.ListArticleRequest) (*pb.ListArticleReply, error) {
	opt := &model.OptionGetArticleList{
		Page:      int(req.Page),
		Size:      int(req.Size_),
		WithCount: true,
		QueryLike: make(map[string][]interface{}),
	}

	_, err := s.checkPermission(ctx)
	if err == nil && req.Wd != "" {
		opt.QueryLike["title"] = []interface{}{req.Wd}
		opt.QueryLike["keywords"] = []interface{}{req.Wd}
		opt.QueryLike["description"] = []interface{}{req.Wd}
		opt.QueryLike["content"] = []interface{}{req.Wd}
	}

	articles, total, err := s.dbModel.GetArticleList(opt)
	if err != nil && err != gorm.ErrRecordNotFound {
		s.logger.Error("ListArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "获取文章列表失败")
	}

	var pbArticle []*pb.Article
	err = util.CopyStruct(articles, &pbArticle)
	if err != nil {
		s.logger.Error("ListArticle", zap.Error(err))
		return nil, status.Errorf(codes.Internal, "获取文章列表失败"+err.Error())
	}

	return &pb.ListArticleReply{
		Total:   total,
		Article: pbArticle,
	}, nil
}
