package repository

import (
	"web-qudo-be/app/database"
	"web-qudo-be/app/database/entity"
	"web-qudo-be/app/module/bookmarks/request"
	"web-qudo-be/utils/paginator"

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

type bookmarksRepository struct {
	DB  *database.Database
	Log zerolog.Logger
}

// BookmarksRepository define interface of IBookmarksRepository
type BookmarksRepository interface {
	GetAll(clientId *uuid.UUID, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error)
	FindOne(clientId *uuid.UUID, id uint) (bookmark *entity.Bookmarks, err error)
	FindByUserAndArticle(clientId *uuid.UUID, userId uint, articleId uint) (bookmark *entity.Bookmarks, err error)
	Create(clientId *uuid.UUID, bookmark *entity.Bookmarks) (bookmarkReturn *entity.Bookmarks, err error)
	Update(clientId *uuid.UUID, id uint, bookmark *entity.Bookmarks) (err error)
	Delete(clientId *uuid.UUID, id uint) (err error)
	GetByUserId(clientId *uuid.UUID, userId uint, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error)
	CountByUserId(clientId *uuid.UUID, userId uint) (count int64, err error)
}

func NewBookmarksRepository(db *database.Database, log zerolog.Logger) BookmarksRepository {
	return &bookmarksRepository{
		DB:  db,
		Log: log,
	}
}

// implement interface of IBookmarksRepository
func (_i *bookmarksRepository) GetAll(clientId *uuid.UUID, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error) {
	var count int64

	query := _i.DB.DB.Model(&entity.Bookmarks{}).Preload("User").Preload("Article")

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	// Apply filters
	if req.UserId != nil {
		query = query.Where("user_id = ?", *req.UserId)
	}

	if req.ArticleId != nil {
		query = query.Where("article_id = ?", *req.ArticleId)
	}

	// Count total records
	if err = query.Count(&count).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to count bookmarks")
		return nil, paging, err
	}

	// Apply pagination
	if req.Pagination != nil {
		offset := (req.Pagination.Page - 1) * req.Pagination.Limit
		query = query.Offset(offset).Limit(req.Pagination.Limit)
		paging = *req.Pagination
	}

	// Execute query
	if err = query.Find(&bookmarks).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to get bookmarks")
		return nil, paging, err
	}

	paging.Count = count
	paging = *paginator.Paging(&paging)

	return bookmarks, paging, nil
}

func (_i *bookmarksRepository) FindOne(clientId *uuid.UUID, id uint) (bookmark *entity.Bookmarks, err error) {
	query := _i.DB.DB.Model(&entity.Bookmarks{}).Preload("User").Preload("Article")

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	if err = query.Where("id = ?", id).First(&bookmark).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to find bookmark")
		return nil, err
	}

	return bookmark, nil
}

func (_i *bookmarksRepository) FindByUserAndArticle(clientId *uuid.UUID, userId uint, articleId uint) (bookmark *entity.Bookmarks, err error) {
	query := _i.DB.DB.Model(&entity.Bookmarks{})

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	if err = query.Where("user_id = ? AND article_id = ?", userId, articleId).First(&bookmark).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to find bookmark by user and article")
		return nil, err
	}

	return bookmark, nil
}

func (_i *bookmarksRepository) Create(clientId *uuid.UUID, bookmark *entity.Bookmarks) (bookmarkReturn *entity.Bookmarks, err error) {
	bookmark.ClientId = clientId

	if err = _i.DB.DB.Create(bookmark).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to create bookmark")
		return nil, err
	}

	return bookmark, nil
}

func (_i *bookmarksRepository) Update(clientId *uuid.UUID, id uint, bookmark *entity.Bookmarks) (err error) {
	query := _i.DB.DB.Model(&entity.Bookmarks{})

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	if err = query.Where("id = ?", id).Updates(bookmark).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to update bookmark")
		return err
	}

	return nil
}

func (_i *bookmarksRepository) Delete(clientId *uuid.UUID, id uint) (err error) {
	query := _i.DB.DB.Model(&entity.Bookmarks{})

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	if err = query.Where("id = ?", id).Delete(&entity.Bookmarks{}).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to delete bookmark")
		return err
	}

	return nil
}

func (_i *bookmarksRepository) GetByUserId(clientId *uuid.UUID, userId uint, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error) {
	var count int64

	query := _i.DB.DB.Model(&entity.Bookmarks{}).Preload("User").Preload("Article")

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	// Apply user filter
	query = query.Where("user_id = ?", userId)

	// Apply additional filters
	if req.ArticleId != nil {
		query = query.Where("article_id = ?", *req.ArticleId)
	}

	// Count total records
	if err = query.Count(&count).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to count user bookmarks")
		return nil, paging, err
	}

	// Apply pagination
	if req.Pagination != nil {
		offset := (req.Pagination.Page - 1) * req.Pagination.Limit
		query = query.Offset(offset).Limit(req.Pagination.Limit)
		paging = *req.Pagination
	}

	// Execute query
	if err = query.Find(&bookmarks).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to get user bookmarks")
		return nil, paging, err
	}

	paging.Count = count
	paging = *paginator.Paging(&paging)

	return bookmarks, paging, nil
}

func (_i *bookmarksRepository) CountByUserId(clientId *uuid.UUID, userId uint) (count int64, err error) {
	query := _i.DB.DB.Model(&entity.Bookmarks{})

	// Apply client filter
	if clientId != nil {
		query = query.Where("client_id = ?", clientId)
	}

	// Apply user filter
	query = query.Where("user_id = ?", userId)

	if err = query.Count(&count).Error; err != nil {
		_i.Log.Error().Err(err).Msg("Failed to count user bookmarks")
		return 0, err
	}

	return count, nil
}
