package usecase

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"lune/talentscale/infra/cache"
	"lune/talentscale/internal/domain"

	"github.com/golang-jwt/jwt/v5"
	"github.com/google/uuid"
	"golang.org/x/crypto/bcrypt"
)

type authUsecase struct {
	userRepo  domain.UserRepository
	roleRepo  domain.RoleRepository
	jwtSecret string
}

func NewAuthUsecase(userRepo domain.UserRepository, roleRepo domain.RoleRepository, jwtSecret string) domain.AuthUsecase {
	return &authUsecase{
		userRepo:  userRepo,
		roleRepo:  roleRepo,
		jwtSecret: jwtSecret,
	}
}

func (u *authUsecase) Register(ctx context.Context, user *domain.User) error {
	// Check if user exists
	existed, _ := u.userRepo.GetByEmail(ctx, user.Email)
	if existed != nil {
		return errors.New("user already exists")
	}

	// Hash password
	hashed, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}
	user.Password = string(hashed)
	user.ID = uuid.New()
	user.CreatedAt = time.Now()
	user.UpdatedAt = time.Now()

	return u.userRepo.Create(ctx, user)
}

func (u *authUsecase) Login(ctx context.Context, email, password string) (string, *domain.User, string, []string, error) {
	user, err := u.userRepo.GetByEmail(ctx, email)
	if err != nil {
		return "", nil, "", nil, errors.New("invalid credentials")
	}

	// Compare password
	if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
		return "", nil, "", nil, errors.New("invalid credentials")
	}

	// Fetch role to get role name
	role, err := u.roleRepo.GetByID(ctx, user.RoleID, user.CompanyID)
	if err != nil {
		// Fallback or handle error
		return "", nil, "", nil, errors.New("user role not found")
	}
	roleName := role.Name

	// Fetch permissions
	perms, _ := u.roleRepo.GetPermissionsByRoleID(ctx, user.RoleID)
	permNames := make([]string, 0)
	for _, p := range perms {
		permNames = append(permNames, p.Name)
	}

	// Save permissions to Redis cache (Fire and forget, fail-safe)
	if permJSON, err := json.Marshal(permNames); err == nil {
		cacheKey := fmt.Sprintf("user:permissions:%s", user.ID.String())
		_ = cache.Set(cacheKey, string(permJSON), 24*time.Hour)
	}

	// Generate JWT
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"user_id":      user.ID.String(),
		"company_id":   user.CompanyID,
		"candidate_id": user.CandidateID,
		"role_id":      user.RoleID.String(),
		"role":         roleName, // Original name for internal use
		"exp":          time.Now().Add(time.Hour * 24 * 45).Unix(),
	})

	signed, err := token.SignedString([]byte(u.jwtSecret))

	// Ensure password is cleared
	user.Password = ""

	return signed, user, roleName, permNames, err
}

func (u *authUsecase) GetMe(ctx context.Context, userID uuid.UUID) (*domain.User, error) {
	return u.userRepo.GetByIDGlobal(ctx, userID)
}

func (u *authUsecase) Logout(ctx context.Context) error {
	// For now, simple logout is enough.
	// We could clear Redis permissions cache here if we want to be strict.
	return nil
}
