package service

import (
	"errors"
	"web-qudo-be/app/database/entity"
	articlesRepository "web-qudo-be/app/module/articles/repository"
	"web-qudo-be/app/module/bookmarks/mapper"
	"web-qudo-be/app/module/bookmarks/repository"
	"web-qudo-be/app/module/bookmarks/request"
	"web-qudo-be/app/module/bookmarks/response"
	usersRepository "web-qudo-be/app/module/users/repository"
	"web-qudo-be/utils/paginator"
	utilSvc "web-qudo-be/utils/service"

	"github.com/google/uuid"
	"github.com/rs/zerolog"
)

// BookmarksService
type bookmarksService struct {
	Repo         repository.BookmarksRepository
	ArticlesRepo articlesRepository.ArticlesRepository
	UsersRepo    usersRepository.UsersRepository
	Log          zerolog.Logger
}

// BookmarksService define interface of IBookmarksService
type BookmarksService interface {
	All(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error)
	Show(clientId *uuid.UUID, id uint) (bookmark *response.BookmarksResponse, err error)
	Save(clientId *uuid.UUID, req request.BookmarksCreateRequest, authToken string) (bookmark *entity.Bookmarks, err error)
	Delete(clientId *uuid.UUID, id uint) error
	GetByUserId(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error)
	ToggleBookmark(clientId *uuid.UUID, authToken string, articleId uint) (isBookmarked bool, err error)
	GetBookmarkSummary(clientId *uuid.UUID, authToken string) (summary *response.BookmarksSummaryResponse, err error)
}

// NewBookmarksService init BookmarksService
func NewBookmarksService(
	repo repository.BookmarksRepository,
	articlesRepo articlesRepository.ArticlesRepository,
	usersRepo usersRepository.UsersRepository,
	log zerolog.Logger,
) BookmarksService {
	return &bookmarksService{
		Repo:         repo,
		ArticlesRepo: articlesRepo,
		UsersRepo:    usersRepo,
		Log:          log,
	}
}

// implement interface of IBookmarksService
func (_i *bookmarksService) All(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) {
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		_i.Log.Error().Msg("User not found from auth token")
		return nil, paging, errors.New("user not found")
	}
	req.UserId = &user.ID

	bookmarksEntity, paging, err := _i.Repo.GetAll(clientId, req)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to get all bookmarks")
		return nil, paging, err
	}

	// Convert []*entity.Bookmarks to []entity.Bookmarks
	var bookmarksSlice []entity.Bookmarks
	for _, b := range bookmarksEntity {
		bookmarksSlice = append(bookmarksSlice, *b)
	}
	bookmarks = mapper.ToBookmarksResponseList(bookmarksSlice)
	return bookmarks, paging, nil
}

func (_i *bookmarksService) Show(clientId *uuid.UUID, id uint) (bookmark *response.BookmarksResponse, err error) {
	bookmarkEntity, err := _i.Repo.FindOne(clientId, id)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to show bookmark")
		return nil, err
	}

	bookmark = mapper.ToBookmarksResponse(bookmarkEntity)
	return bookmark, nil
}

func (_i *bookmarksService) Save(clientId *uuid.UUID, req request.BookmarksCreateRequest, authToken string) (bookmark *entity.Bookmarks, err error) {
	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		_i.Log.Error().Msg("User not found from auth token")
		return nil, errors.New("user not found")
	}

	// Check if article exists
	_, err = _i.ArticlesRepo.FindOne(clientId, req.ArticleId)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Article not found")
		return nil, errors.New("article not found")
	}

	// Check if bookmark already exists
	existingBookmark, err := _i.Repo.FindByUserAndArticle(clientId, user.ID, req.ArticleId)
	if err == nil && existingBookmark != nil {
		_i.Log.Error().Msg("Bookmark already exists")
		return nil, errors.New("article already bookmarked")
	}

	// Create new bookmark
	bookmarkEntity := req.ToEntity(user.ID)
	bookmark, err = _i.Repo.Create(clientId, bookmarkEntity)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to create bookmark")
		return nil, err
	}

	return bookmark, nil
}

func (_i *bookmarksService) Delete(clientId *uuid.UUID, id uint) error {
	err := _i.Repo.Delete(clientId, id)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to delete bookmark")
		return err
	}

	return nil
}

func (_i *bookmarksService) GetByUserId(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) {
	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		_i.Log.Error().Msg("User not found from auth token")
		return nil, paging, errors.New("user not found")
	}

	bookmarksEntity, paging, err := _i.Repo.GetByUserId(clientId, user.ID, req)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to get bookmarks by user ID")
		return nil, paging, err
	}

	// Convert []*entity.Bookmarks to []entity.Bookmarks
	var bookmarksSlice []entity.Bookmarks
	for _, b := range bookmarksEntity {
		bookmarksSlice = append(bookmarksSlice, *b)
	}
	bookmarks = mapper.ToBookmarksResponseList(bookmarksSlice)
	return bookmarks, paging, nil
}

func (_i *bookmarksService) ToggleBookmark(clientId *uuid.UUID, authToken string, articleId uint) (isBookmarked bool, err error) {
	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		_i.Log.Error().Msg("User not found from auth token")
		return false, errors.New("user not found")
	}

	// Check if article exists
	_, err = _i.ArticlesRepo.FindOne(clientId, articleId)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Article not found")
		return false, errors.New("article not found")
	}

	// Check if bookmark already exists
	existingBookmark, err := _i.Repo.FindByUserAndArticle(clientId, user.ID, articleId)
	if err == nil && existingBookmark != nil {
		// Bookmark exists, delete it
		err = _i.Repo.Delete(clientId, existingBookmark.ID)
		if err != nil {
			_i.Log.Error().Err(err).Msg("Failed to delete existing bookmark")
			return false, err
		}
		return false, nil // Bookmark removed
	}

	// Bookmark doesn't exist, create it
	bookmarkEntity := &entity.Bookmarks{
		UserId:    user.ID,
		ArticleId: articleId,
		IsActive:  boolPtr(true),
	}

	_, err = _i.Repo.Create(clientId, bookmarkEntity)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to create bookmark")
		return false, err
	}

	return true, nil // Bookmark added
}

func (_i *bookmarksService) GetBookmarkSummary(clientId *uuid.UUID, authToken string) (summary *response.BookmarksSummaryResponse, err error) {
	// Extract user info from auth token
	user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
	if user == nil {
		_i.Log.Error().Msg("User not found from auth token")
		return nil, errors.New("user not found")
	}

	// Get total count
	totalCount, err := _i.Repo.CountByUserId(clientId, user.ID)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to count user bookmarks")
		return nil, err
	}

	// Get recent bookmarks (last 5)
	req := request.BookmarksQueryRequest{
		Pagination: &paginator.Pagination{
			Page:  1,
			Limit: 5,
		},
	}

	recentBookmarksEntity, _, err := _i.Repo.GetByUserId(clientId, user.ID, req)
	if err != nil {
		_i.Log.Error().Err(err).Msg("Failed to get recent bookmarks")
		return nil, err
	}

	// Convert []*entity.Bookmarks to []entity.Bookmarks
	var bookmarksSlice []entity.Bookmarks
	for _, b := range recentBookmarksEntity {
		bookmarksSlice = append(bookmarksSlice, *b)
	}
	recentBookmarks := mapper.ToBookmarksResponseList(bookmarksSlice)

	summary = &response.BookmarksSummaryResponse{
		TotalBookmarks:  int(totalCount),
		RecentBookmarks: recentBookmarks,
	}

	return summary, nil
}

// Helper function to create bool pointer
func boolPtr(b bool) *bool {
	return &b
}
