package services

import (
	"api-service/internal/config"
	models "api-service/internal/models/auth"
	"errors"
	"time"

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

// AuthService handles authentication logic
type AuthService struct {
	config *config.Config
	users  map[string]*models.User // In-memory user store for demo
}

// NewAuthService creates a new authentication service
func NewAuthService(cfg *config.Config) *AuthService {
	// Initialize with demo users
	users := make(map[string]*models.User)

	// Add demo users
	users["admin"] = &models.User{
		ID:       "1",
		Username: "admin",
		Email:    "admin@example.com",
		Password: "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", // password
		Role:     "admin",
	}

	users["user"] = &models.User{
		ID:       "2",
		Username: "user",
		Email:    "user@example.com",
		Password: "$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi", // password
		Role:     "user",
	}

	return &AuthService{
		config: cfg,
		users:  users,
	}
}

// Login authenticates user and generates JWT token
func (s *AuthService) Login(username, password string) (*models.TokenResponse, error) {
	user, exists := s.users[username]
	if !exists {
		return nil, errors.New("invalid credentials")
	}

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

	// Generate JWT token
	token, err := s.generateToken(user)
	if err != nil {
		return nil, err
	}

	return &models.TokenResponse{
		AccessToken: token,
		TokenType:   "Bearer",
		ExpiresIn:   3600, // 1 hour
	}, nil
}

// generateToken creates a new JWT token for the user
func (s *AuthService) generateToken(user *models.User) (string, error) {
	// Create claims
	claims := jwt.MapClaims{
		"user_id":  user.ID,
		"username": user.Username,
		"email":    user.Email,
		"role":     user.Role,
		"exp":      time.Now().Add(time.Hour * 1).Unix(),
		"iat":      time.Now().Unix(),
	}

	// Create token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Sign token with secret key
	secretKey := []byte(s.getJWTSecret())
	return token.SignedString(secretKey)
}

// GenerateTokenForUser generates a JWT token for a specific user
func (s *AuthService) GenerateTokenForUser(user *models.User) (string, error) {
	// Create claims
	claims := jwt.MapClaims{
		"user_id":  user.ID,
		"username": user.Username,
		"email":    user.Email,
		"role":     user.Role,
		"exp":      time.Now().Add(time.Hour * 1).Unix(),
		"iat":      time.Now().Unix(),
	}

	// Create token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// Sign token with secret key
	secretKey := []byte(s.getJWTSecret())
	return token.SignedString(secretKey)
}

// ValidateToken validates the JWT token
func (s *AuthService) ValidateToken(tokenString string) (*models.JWTClaims, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.New("unexpected signing method")
		}
		return []byte(s.getJWTSecret()), nil
	})

	if err != nil {
		return nil, err
	}

	if !token.Valid {
		return nil, errors.New("invalid token")
	}

	claims, ok := token.Claims.(jwt.MapClaims)
	if !ok {
		return nil, errors.New("invalid claims")
	}

	return &models.JWTClaims{
		UserID:   claims["user_id"].(string),
		Username: claims["username"].(string),
		Email:    claims["email"].(string),
		Role:     claims["role"].(string),
	}, nil
}

// getJWTSecret returns the JWT secret key
func (s *AuthService) getJWTSecret() string {
	// In production, this should come from environment variables
	return "your-secret-key-change-this-in-production"
}

// RegisterUser registers a new user (for demo purposes)
func (s *AuthService) RegisterUser(username, email, password, role string) error {
	if _, exists := s.users[username]; exists {
		return errors.New("username already exists")
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	if err != nil {
		return err
	}

	s.users[username] = &models.User{
		ID:       string(rune(len(s.users) + 1)),
		Username: username,
		Email:    email,
		Password: string(hashedPassword),
		Role:     role,
	}

	return nil
}
