package service

import (
	"context"
	"fmt"
	"sort"
	"time"

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

type CommentRepository interface {
	GetCommentByID(ctx context.Context, id int64) (*entity.Comment, error)
	InsertComment(ctx context.Context, comment *entity.Comment) (*entity.Comment, error)
	ListCommentsBySku(ctx context.Context, sku int64) ([]*entity.Comment, error)
	ListCommentsByUser(ctx context.Context, userID int64) ([]*entity.Comment, error)
	UpdateComment(ctx context.Context, comment *entity.Comment) (*entity.Comment, error)
}

type CommentsService struct {
	comments CommentRepository

	timeout time.Duration
}

func NewCommentsService(commRepo CommentRepository, timeout time.Duration) *CommentsService {
	return &CommentsService{
		comments: commRepo,
		timeout:  timeout,
	}
}

func (s *CommentsService) checkTimeout(newTime, oldTime time.Time) error {
	if newTime.Sub(oldTime) >= s.timeout {
		return model.ErrCommentEditTimeout
	}

	return nil
}

func (s *CommentsService) CommentCreate(ctx context.Context, comment *entity.Comment) (int64, error) {
	comm, err := s.comments.InsertComment(ctx, &entity.Comment{
		UserID:    comment.UserID,
		SKU:       comment.SKU,
		CreatedAt: time.Now(),
		Text:      comment.Text,
	})
	if err != nil {
		return 0, fmt.Errorf("repository.InsertComment: %w", err)
	}

	return comm.ID, nil
}

func (s *CommentsService) CommentGetByID(ctx context.Context, id int64) (*entity.Comment, error) {
	if id <= 0 {
		return nil, fmt.Errorf("comment id must be greater than 0")
	}

	comm, err := s.comments.GetCommentByID(ctx, id)
	if err != nil {
		return nil, fmt.Errorf("repository.InsertComment: %w", err)
	}

	return comm, nil
}

func (s *CommentsService) CommentEdit(ctx context.Context, newComment *entity.Comment) error {
	if newComment.ID <= 0 {
		return fmt.Errorf("comment id must be greater than 0")
	}

	oldComment, err := s.comments.GetCommentByID(ctx, newComment.ID)
	if err != nil {
		return fmt.Errorf("repository.GetCommentByID: %w", err)
	}

	if oldComment.UserID != newComment.UserID {
		return model.ErrCommentEditUserMismatch
	}

	comment := &entity.Comment{
		ID:        newComment.ID,
		UserID:    newComment.UserID,
		SKU:       newComment.SKU,
		CreatedAt: time.Now(),
		Text:      newComment.Text,
	}

	if err := s.checkTimeout(comment.CreatedAt, oldComment.CreatedAt); err != nil {
		return err
	}

	if _, err := s.comments.UpdateComment(ctx, comment); err != nil {
		return fmt.Errorf("repository.UpdateComment: %w", err)
	}

	return nil
}

func (s *CommentsService) CommentListBySKU(ctx context.Context, sku int64) ([]*entity.Comment, error) {
	if sku <= 0 {
		return nil, fmt.Errorf("sku must be greater than 0")
	}

	comms, err := s.comments.ListCommentsBySku(ctx, sku)
	if err != nil {
		return nil, fmt.Errorf("repository.ListCommentsBySku: %w", err)
	}

	return comms, nil
}

func (s *CommentsService) CommentListByUser(ctx context.Context, userID int64) ([]*entity.Comment, error) {
	if userID <= 0 {
		return nil, fmt.Errorf("userID must be greater than 0")
	}

	comms, err := s.comments.ListCommentsByUser(ctx, userID)
	if err != nil {
		return nil, fmt.Errorf("repository.ListCommentsByUser: %w", err)
	}

	sort.Slice(comms, func(i, j int) bool {
		if comms[i].CreatedAt.Equal(comms[j].CreatedAt) {
			return comms[i].UserID < comms[j].UserID
		}

		return comms[i].CreatedAt.After(comms[j].CreatedAt)
	})

	return comms, nil
}
