package services

import (
	"fmt"
	"time"

	"rijig/internal/repositories"
	"rijig/model"
	"rijig/utils"

	"golang.org/x/crypto/bcrypt"
)

type UserPinService interface {
	CreateUserPin(userID, pin string) (string, error)
	VerifyUserPin(userID, pin string) (string, error)
	CheckPinStatus(userID string) (string, error)
	UpdateUserPin(userID, oldPin, newPin string) (string, error)
}

type userPinService struct {
	UserPinRepo repositories.UserPinRepository
}

func NewUserPinService(userPinRepo repositories.UserPinRepository) UserPinService {
	return &userPinService{UserPinRepo: userPinRepo}
}

func (s *userPinService) VerifyUserPin(userID, pin string) (string, error) {
	if pin == "" {
		return "", fmt.Errorf("pin tidak boleh kosong")
	}

	userPin, err := s.UserPinRepo.FindByUserID(userID)
	if err != nil {
		return "", fmt.Errorf("error fetching user pin: %v", err)
	}
	if userPin == nil {
		return "", fmt.Errorf("user pin not found")
	}

	err = bcrypt.CompareHashAndPassword([]byte(userPin.Pin), []byte(pin))
	if err != nil {
		return "", fmt.Errorf("incorrect pin")
	}

	return "Pin yang anda masukkan benar", nil
}

func (s *userPinService) CheckPinStatus(userID string) (string, error) {
	userPin, err := s.UserPinRepo.FindByUserID(userID)
	if err != nil {
		return "", fmt.Errorf("error checking pin status: %v", err)
	}
	if userPin == nil {
		return "Pin not created", nil
	}

	return "Pin already created", nil
}

func (s *userPinService) CreateUserPin(userID, pin string) (string, error) {

	existingPin, err := s.UserPinRepo.FindByUserID(userID)
	if err != nil {
		return "", fmt.Errorf("error checking existing pin: %v", err)
	}

	if existingPin != nil {
		return "", fmt.Errorf("you have already created a pin, you don't need to create another one")
	}

	hashedPin, err := bcrypt.GenerateFromPassword([]byte(pin), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("error hashing the pin: %v", err)
	}

	newPin := model.UserPin{
		UserID: userID,
		Pin:    string(hashedPin),
	}

	err = s.UserPinRepo.Create(&newPin)
	if err != nil {
		return "", fmt.Errorf("error creating user pin: %v", err)
	}

	cacheKey := fmt.Sprintf("userpin:%s", userID)
	cacheData := map[string]interface{}{"data": newPin}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching new user pin to Redis: %v\n", err)
	}

	return "Pin berhasil dibuat", nil
}

func (s *userPinService) UpdateUserPin(userID, oldPin, newPin string) (string, error) {

	userPin, err := s.UserPinRepo.FindByUserID(userID)
	if err != nil {
		return "", fmt.Errorf("error fetching user pin: %v", err)
	}

	if userPin == nil {
		return "", fmt.Errorf("user pin not found")
	}

	err = bcrypt.CompareHashAndPassword([]byte(userPin.Pin), []byte(oldPin))
	if err != nil {
		return "", fmt.Errorf("incorrect old pin")
	}

	hashedPin, err := bcrypt.GenerateFromPassword([]byte(newPin), bcrypt.DefaultCost)
	if err != nil {
		return "", fmt.Errorf("error hashing the new pin: %v", err)
	}

	userPin.Pin = string(hashedPin)
	err = s.UserPinRepo.Update(userPin)
	if err != nil {
		return "", fmt.Errorf("error updating user pin: %v", err)
	}

	cacheKey := fmt.Sprintf("userpin:%s", userID)
	cacheData := map[string]interface{}{"data": userPin}
	err = utils.SetJSONData(cacheKey, cacheData, time.Hour*24)
	if err != nil {
		fmt.Printf("Error caching updated user pin to Redis: %v\n", err)
	}

	return "Pin berhasil diperbarui", nil
}
