package service

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"narasi-ahli-be/app/module/education_history/mapper"
	"narasi-ahli-be/app/module/education_history/repository"
	"narasi-ahli-be/app/module/education_history/request"
	"narasi-ahli-be/app/module/education_history/response"
	usersRepository "narasi-ahli-be/app/module/users/repository"
	"narasi-ahli-be/config/config"
	minioStorage "narasi-ahli-be/config/config"
	"narasi-ahli-be/utils/paginator"

	"github.com/gofiber/fiber/v2"
	"github.com/minio/minio-go/v7"
	"github.com/rs/zerolog"
)

type educationHistoryService struct {
	Repo         repository.EducationHistoryRepository
	UsersRepo    usersRepository.UsersRepository
	Log          zerolog.Logger
	Cfg          *config.Config
	MinioStorage *minioStorage.MinioStorage
}

type EducationHistoryService interface {
	All(req request.EducationHistoryQueryRequest) (educationHistories []*response.EducationHistoryResponse, paging paginator.Pagination, err error)
	Show(userId uint, id uint) (educationHistory *response.EducationHistoryResponse, err error)
	Save(userId uint, req request.EducationHistoryCreateRequest) (educationHistory *response.EducationHistoryResponse, err error)
	Update(userId uint, id uint, req request.EducationHistoryUpdateRequest) (err error)
	Delete(userId uint, id uint) error
	UploadCertificate(userId uint, id uint, c *fiber.Ctx) error
}

func NewEducationHistoryService(repo repository.EducationHistoryRepository, usersRepo usersRepository.UsersRepository, log zerolog.Logger, cfg *config.Config, minioStorage *minioStorage.MinioStorage) EducationHistoryService {
	return &educationHistoryService{
		Repo:         repo,
		UsersRepo:    usersRepo,
		Log:          log,
		Cfg:          cfg,
		MinioStorage: minioStorage,
	}
}

func (_i *educationHistoryService) All(req request.EducationHistoryQueryRequest) (educationHistories []*response.EducationHistoryResponse, paging paginator.Pagination, err error) {
	results, paging, err := _i.Repo.GetAll(req.UserID, req)
	if err != nil {
		return
	}

	for _, result := range results {
		educationHistories = append(educationHistories, mapper.EducationHistoryResponseMapper(result))
	}

	return
}

func (_i *educationHistoryService) Show(userId uint, id uint) (educationHistory *response.EducationHistoryResponse, err error) {
	result, err := _i.Repo.FindOneByUserAndId(userId, id)
	if err != nil {
		return nil, err
	}

	return mapper.EducationHistoryResponseMapper(result), nil
}

func (_i *educationHistoryService) Save(userId uint, req request.EducationHistoryCreateRequest) (educationHistory *response.EducationHistoryResponse, err error) {
	_i.Log.Info().Interface("data", req).Msg("Creating education history")

	entity := req.ToEntity(userId)

	result, err := _i.Repo.Create(entity)
	if err != nil {
		return nil, err
	}

	return mapper.EducationHistoryResponseMapper(result), nil
}

func (_i *educationHistoryService) Update(userId uint, id uint, req request.EducationHistoryUpdateRequest) (err error) {
	_i.Log.Info().Interface("data", req).Msg("Updating education history")

	// Check if record exists and belongs to user
	existing, err := _i.Repo.FindOneByUserAndId(userId, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("education history not found")
	}

	entity := req.ToEntity(userId)
	return _i.Repo.Update(userId, id, entity)
}

func (_i *educationHistoryService) Delete(userId uint, id uint) error {
	_i.Log.Info().Uint("userId", userId).Uint("id", id).Msg("Deleting education history")

	// Check if record exists and belongs to user
	existing, err := _i.Repo.FindOneByUserAndId(userId, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("education history not found")
	}

	return _i.Repo.Delete(userId, id)
}

func (_i *educationHistoryService) UploadCertificate(userId uint, id uint, c *fiber.Ctx) error {
	_i.Log.Info().Uint("userId", userId).Uint("id", id).Msg("Uploading certificate")

	// Check if record exists and belongs to user
	existing, err := _i.Repo.FindOneByUserAndId(userId, id)
	if err != nil {
		return err
	}
	if existing == nil {
		return errors.New("education history not found")
	}

	// Get multipart form
	form, err := c.MultipartForm()
	if err != nil {
		return err
	}

	// Get file from form
	files := form.File["certificate"]
	if len(files) == 0 {
		return errors.New("no certificate file provided")
	}

	fileHeader := files[0]
	bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName

	// Create minio connection
	minioClient, err := _i.MinioStorage.ConnectMinio()
	if err != nil {
		return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
			"error": true,
			"msg":   err.Error(),
		})
	}

	// Process file
	src, err := fileHeader.Open()
	if err != nil {
		return err
	}
	defer src.Close()

	filename := filepath.Base(fileHeader.Filename)
	filename = strings.ReplaceAll(filename, " ", "")
	filenameWithoutExt := filepath.Clean(filename[:len(filename)-len(filepath.Ext(filename))])
	extension := filepath.Ext(fileHeader.Filename)[1:]

	now := time.Now()
	rand.New(rand.NewSource(now.UnixNano()))
	randUniqueId := rand.Intn(1000000)

	newFilenameWithoutExt := filenameWithoutExt + "_" + strconv.Itoa(randUniqueId)
	newFilename := newFilenameWithoutExt + "." + extension

	objectName := fmt.Sprintf("education-history/certificates/%d/%d/%s", now.Year(), now.Month(), newFilename)

	// Upload file to MinIO
	_, err = minioClient.PutObject(context.Background(), bucketName, objectName, src, fileHeader.Size, minio.PutObjectOptions{})
	if err != nil {
		return err
	}

	// Update certificate image path with MinIO object name
	existing.CertificateImage = &objectName
	return _i.Repo.Update(userId, id, existing)
}
