package services

import (
	"context"
	"fmt"
	"time"

	"go.uber.org/zap"

	"github.com/kms/api-key-service/internal/auth"
	"github.com/kms/api-key-service/internal/config"
	"github.com/kms/api-key-service/internal/domain"
	"github.com/kms/api-key-service/internal/errors"
)

// authenticationService implements the AuthenticationService interface
type authenticationService struct {
	config     config.ConfigProvider
	logger     *zap.Logger
	jwtManager *auth.JWTManager
}

// NewAuthenticationService creates a new authentication service
func NewAuthenticationService(config config.ConfigProvider, logger *zap.Logger) AuthenticationService {
	jwtManager := auth.NewJWTManager(config, logger)
	return &authenticationService{
		config:     config,
		logger:     logger,
		jwtManager: jwtManager,
	}
}

// GetUserID extracts user ID from context
func (s *authenticationService) GetUserID(ctx context.Context) (string, error) {
	// For now, this is a simple implementation
	// In a real implementation, this would extract from JWT tokens, session, etc.
	
	if userID, ok := ctx.Value("user_id").(string); ok {
		return userID, nil
	}
	
	return "", fmt.Errorf("user ID not found in context")
}

// ValidatePermissions checks if user has required permissions
func (s *authenticationService) ValidatePermissions(ctx context.Context, userID string, appID string, requiredPermissions []string) error {
	s.logger.Debug("Validating permissions", 
		zap.String("user_id", userID), 
		zap.String("app_id", appID), 
		zap.Strings("required_permissions", requiredPermissions))

	// TODO: Implement actual permission validation
	// For now, we'll just allow all requests
	
	return nil
}

// GetUserClaims retrieves user claims
func (s *authenticationService) GetUserClaims(ctx context.Context, userID string) (map[string]string, error) {
	s.logger.Debug("Getting user claims", zap.String("user_id", userID))

	// TODO: Implement actual claims retrieval
	// For now, return basic claims
	
	claims := map[string]string{
		"user_id": userID,
		"email":   userID, // Assuming user_id is email for now
		"name":    "Test User",
	}
	
	return claims, nil
}

// ValidateJWTToken validates a JWT token and returns claims
func (s *authenticationService) ValidateJWTToken(ctx context.Context, tokenString string) (*domain.AuthContext, error) {
	s.logger.Debug("Validating JWT token")

	// Validate the token using JWT manager
	claims, err := s.jwtManager.ValidateToken(tokenString)
	if err != nil {
		s.logger.Warn("JWT token validation failed", zap.Error(err))
		return nil, err
	}

	// Check if token is revoked
	revoked, err := s.jwtManager.IsTokenRevoked(tokenString)
	if err != nil {
		s.logger.Error("Failed to check token revocation status", zap.Error(err))
		return nil, errors.NewInternalError("Failed to validate token").WithInternal(err)
	}

	if revoked {
		s.logger.Warn("JWT token is revoked", zap.String("user_id", claims.UserID))
		return nil, errors.NewAuthenticationError("Token has been revoked")
	}

	// Convert JWT claims to AuthContext
	authContext := &domain.AuthContext{
		UserID:      claims.UserID,
		TokenType:   claims.TokenType,
		Permissions: claims.Permissions,
		Claims:      claims.Claims,
		AppID:       claims.AppID,
	}

	s.logger.Debug("JWT token validated successfully", 
		zap.String("user_id", claims.UserID),
		zap.String("app_id", claims.AppID))

	return authContext, nil
}

// GenerateJWTToken generates a new JWT token for a user
func (s *authenticationService) GenerateJWTToken(ctx context.Context, userToken *domain.UserToken) (string, error) {
	s.logger.Debug("Generating JWT token", 
		zap.String("user_id", userToken.UserID),
		zap.String("app_id", userToken.AppID))

	// Generate the token using JWT manager
	tokenString, err := s.jwtManager.GenerateToken(userToken)
	if err != nil {
		s.logger.Error("Failed to generate JWT token", zap.Error(err))
		return "", err
	}

	s.logger.Debug("JWT token generated successfully", 
		zap.String("user_id", userToken.UserID),
		zap.String("app_id", userToken.AppID))

	return tokenString, nil
}

// RefreshJWTToken refreshes an existing JWT token
func (s *authenticationService) RefreshJWTToken(ctx context.Context, tokenString string, newExpiration time.Time) (string, error) {
	s.logger.Debug("Refreshing JWT token")

	// Refresh the token using JWT manager
	newTokenString, err := s.jwtManager.RefreshToken(tokenString, newExpiration)
	if err != nil {
		s.logger.Error("Failed to refresh JWT token", zap.Error(err))
		return "", err
	}

	s.logger.Debug("JWT token refreshed successfully")

	return newTokenString, nil
}
