package repository

import (
	"fmt"
	"jaecoo-be/app/database"
	"jaecoo-be/app/database/entity"
	"jaecoo-be/app/module/feedbacks/request"
	"jaecoo-be/app/module/feedbacks/response"
	"jaecoo-be/utils/paginator"
	utilSvc "jaecoo-be/utils/service"
	"strings"
	"time"

	"github.com/rs/zerolog"
)

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

// FeedbacksRepository define interface of IFeedbacksRepository
type FeedbacksRepository interface {
	GetAll(req request.FeedbacksQueryRequest) (feedbackss []*entity.Feedbacks, paging paginator.Pagination, err error)
	FindOne(id uint) (feedbacks *entity.Feedbacks, err error)
	Create(feedbacks *entity.Feedbacks) (feedbacksReturn *entity.Feedbacks, err error)
	Update(id uint, feedbacks *entity.Feedbacks) (err error)
	Delete(id uint) (err error)
	FeedbacksMonthlyStats(year int) (feedbacksMonthlyStats []*response.FeedbacksMonthlyStats, err error)
}

func NewFeedbacksRepository(db *database.Database, logger zerolog.Logger) FeedbacksRepository {
	return &feedbacksRepository{
		DB:  db,
		Log: logger,
	}
}

// implement interface of IFeedbacksRepository
func (_i *feedbacksRepository) GetAll(req request.FeedbacksQueryRequest) (feedbackss []*entity.Feedbacks, paging paginator.Pagination, err error) {
	var count int64

	query := _i.DB.DB.Model(&entity.Feedbacks{})

	query = query.Where("is_active = ?", true)

	if req.Message != nil && *req.Message != "" {
		message := strings.ToLower(*req.Message)
		query = query.Where("LOWER(message) LIKE ?", "%"+strings.ToLower(message)+"%")
	}
	if req.CommentFromName != nil && *req.CommentFromName != "" {
		commentFromName := strings.ToLower(*req.CommentFromName)
		query = query.Where("LOWER(comment_from_name) LIKE ?", "%"+strings.ToLower(commentFromName)+"%")
	}
	if req.CommentFromEmail != nil && *req.CommentFromEmail != "" {
		commentFromEmail := strings.ToLower(*req.CommentFromEmail)
		query = query.Where("LOWER(comment_from_email) LIKE ?", "%"+strings.ToLower(commentFromEmail)+"%")
	}
	if req.StatusId != nil {
		query = query.Where("status_id = ?", req.StatusId)
	}

	query.Count(&count)

	if req.Pagination.SortBy != "" {
		direction := "ASC"
		if req.Pagination.Sort == "desc" {
			direction = "DESC"
		}
		query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction))
	}

	req.Pagination.Count = count
	req.Pagination = paginator.Paging(req.Pagination)

	err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&feedbackss).Error
	if err != nil {
		return
	}

	paging = *req.Pagination

	return
}

func (_i *feedbacksRepository) FindOne(id uint) (feedbacks *entity.Feedbacks, err error) {
	query := _i.DB.DB.Where("id = ?", id)

	if err := query.First(&feedbacks).Error; err != nil {
		return nil, err
	}

	return feedbacks, nil
}

func (_i *feedbacksRepository) Create(feedbacks *entity.Feedbacks) (feedbacksReturn *entity.Feedbacks, err error) {
	result := _i.DB.DB.Create(feedbacks)
	return feedbacks, result.Error
}

func (_i *feedbacksRepository) Update(id uint, feedbacks *entity.Feedbacks) (err error) {
	feedbacksMap, err := utilSvc.StructToMap(feedbacks)
	if err != nil {
		return err
	}
	query := _i.DB.DB.Model(&entity.Feedbacks{}).Where(&entity.Feedbacks{ID: id})
	return query.Updates(feedbacksMap).Error
}

func (_i *feedbacksRepository) Delete(id uint) error {
	query := _i.DB.DB.Model(&entity.Feedbacks{}).Where("id = ?", id)
	return query.Delete(&entity.Feedbacks{}).Error
}

func (_i *feedbacksRepository) FeedbacksMonthlyStats(year int) (feedbacksMonthlyStats []*response.FeedbacksMonthlyStats, err error) {

	if year < 1900 || year > 2100 {
		return nil, fmt.Errorf("invalid year")
	}

	var results []struct {
		Month          int
		Day            int
		TotalFeedbacks int
	}

	query := _i.DB.DB.Model(&entity.Feedbacks{}).
		Select("EXTRACT(MONTH FROM created_at) as month, EXTRACT(DAY FROM created_at) as day, "+
			"count(id) as total_feedbacks").
		Where("EXTRACT(YEAR FROM created_at) = ?", year)

	err = query.Group("month, day").Scan(&results).Error
	if err != nil {
		return nil, err
	}

	// Siapkan struktur untuk menyimpan data bulanan
	feedbackStats := make([]*response.FeedbacksMonthlyStats, 12)
	for i := 0; i < 12; i++ {
		daysInMonth := time.Date(year, time.Month(i+1), 0, 0, 0, 0, 0, time.UTC).Day()
		feedbackStats[i] = &response.FeedbacksMonthlyStats{
			Year:        year,
			Month:       i + 1,
			Suggestions: make([]int, daysInMonth),
		}
	}

	// Isi data dari hasil agregasi
	for _, result := range results {
		monthIndex := result.Month - 1
		dayIndex := result.Day - 1

		if monthIndex >= 0 && monthIndex < 12 {
			if dayIndex >= 0 && dayIndex < len(feedbackStats[monthIndex].Suggestions) {
				feedbackStats[monthIndex].Suggestions[dayIndex] = result.TotalFeedbacks
			}
		}
	}

	return feedbackStats, nil
}
