package server

import (
	"context"
	"errors"
	"fmt"

	"route256/comments/internal/domain/entity"
	"route256/comments/internal/domain/model"

	pb "route256/pkg/api/comments/v1"

	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"google.golang.org/protobuf/types/known/emptypb"
	"google.golang.org/protobuf/types/known/timestamppb"
)

var _ pb.CommentsServer = (*Server)(nil)

type CommentsService interface {
	CommentGetByID(ctx context.Context, id int64) (*entity.Comment, error)
	CommentCreate(ctx context.Context, comment *entity.Comment) (int64, error)
	CommentListBySKU(ctx context.Context, sku int64) ([]*entity.Comment, error)
	CommentListByUser(ctx context.Context, userID int64) ([]*entity.Comment, error)
	CommentEdit(ctx context.Context, comment *entity.Comment) error
}

type Server struct {
	pb.UnimplementedCommentsServer

	service CommentsService
}

func NewServer(commentsService CommentsService) *Server {
	return &Server{
		service: commentsService,
	}
}

func (s *Server) CommentAdd(ctx context.Context, req *pb.CreateCommentRequest) (*pb.CreateCommentResponse, error) {
	id, err := s.service.CommentCreate(ctx, &entity.Comment{
		UserID: req.UserId,
		SKU:    req.Sku,
		Text:   req.Comment,
	})
	if err != nil {
		return nil, fmt.Errorf("service.InsertComment: %w", err)
	}

	return &pb.CreateCommentResponse{
		Id: id,
	}, nil
}

func (s *Server) CommentEdit(ctx context.Context, req *pb.EditCommentRequest) (*emptypb.Empty, error) {
	err := s.service.CommentEdit(ctx, &entity.Comment{
		ID:     req.CommentId,
		UserID: req.UserId,
		Text:   req.NewComment,
	})
	switch {
	case errors.Is(err, model.ErrCommentEditUserMismatch):
		return &emptypb.Empty{}, status.Error(codes.PermissionDenied, err.Error())
	case errors.Is(err, model.ErrCommentEditTimeout):
		return &emptypb.Empty{}, status.Error(codes.FailedPrecondition, err.Error())
	case err != nil:
		return &emptypb.Empty{}, status.Error(codes.Internal, err.Error())
	}

	return &emptypb.Empty{}, nil
}

func (s *Server) CommentGetByID(ctx context.Context, req *pb.GetCommentRequest) (*pb.GetCommentResponse, error) {
	comm, err := s.service.CommentGetByID(ctx, req.Id)
	if err != nil {
		return nil, fmt.Errorf("service.GetCommentByID: %w", err)
	}

	return &pb.GetCommentResponse{
		Comment: &pb.Comment{
			Id:        comm.ID,
			UserId:    comm.UserID,
			Sku:       comm.SKU,
			Text:      comm.Text,
			CreatedAt: timestamppb.New(comm.CreatedAt),
		},
	}, nil
}

func (s *Server) CommentListBySKU(ctx context.Context, req *pb.ListBySkuRequest) (*pb.ListBySkuResponse, error) {
	comms, err := s.service.CommentListBySKU(ctx, req.Sku)
	if err != nil {
		return nil, fmt.Errorf("service.ListCommentsBySku: %w", err)
	}

	comments := make([]*pb.Comment, len(comms))
	for i, comm := range comms {
		comments[i] = &pb.Comment{
			Id:        comm.ID,
			UserId:    comm.UserID,
			Sku:       comm.SKU,
			Text:      comm.Text,
			CreatedAt: timestamppb.New(comm.CreatedAt),
		}
	}

	return &pb.ListBySkuResponse{
		Comments: comments,
	}, nil
}

func (s *Server) CommentListByUser(ctx context.Context, req *pb.ListByUserRequest) (*pb.ListByUserResponse, error) {
	comms, err := s.service.CommentListByUser(ctx, req.UserId)
	if err != nil {
		return nil, fmt.Errorf("service.ListCommentsByUser: %w", err)
	}

	comments := make([]*pb.Comment, len(comms))
	for i, comm := range comms {
		comments[i] = &pb.Comment{
			Id:        comm.ID,
			UserId:    comm.UserID,
			Sku:       comm.SKU,
			Text:      comm.Text,
			CreatedAt: timestamppb.New(comm.CreatedAt),
		}
	}

	return &pb.ListByUserResponse{
		Comments: comments,
	}, nil
}
