package handlers

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"

	"github.com/RyanCopley/skybridge/kms/internal/domain"
	"github.com/RyanCopley/skybridge/kms/internal/errors"
	"github.com/RyanCopley/skybridge/kms/internal/services"
	"github.com/RyanCopley/skybridge/kms/internal/validation"
)

// TokenHandler handles token-related HTTP requests
type TokenHandler struct {
	tokenService services.TokenService
	authService  services.AuthenticationService
	validator    *validation.Validator
	errorHandler *errors.ErrorHandler
	logger       *zap.Logger
}

// NewTokenHandler creates a new token handler
func NewTokenHandler(
	tokenService services.TokenService,
	authService services.AuthenticationService,
	logger *zap.Logger,
) *TokenHandler {
	return &TokenHandler{
		tokenService: tokenService,
		authService:  authService,
		validator:    validation.NewValidator(logger),
		errorHandler: errors.NewErrorHandler(logger),
		logger:       logger,
	}
}

// Create handles POST /applications/:id/tokens
func (h *TokenHandler) Create(c *gin.Context) {
	// Validate application ID parameter
	appID := c.Param("id")
	if appID == "" {
		h.errorHandler.HandleValidationError(c, "id", "Application ID is required")
		return
	}

	// Bind and validate JSON request
	var req domain.CreateStaticTokenRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Warn("Invalid request body", zap.Error(err))
		h.errorHandler.HandleValidationError(c, "request_body", "Invalid request body format")
		return
	}

	// Set app ID from URL parameter
	req.AppID = appID

	// Basic validation - the service layer will do more comprehensive validation
	if req.AppID == "" {
		h.errorHandler.HandleValidationError(c, "app_id", "Application ID is required")
		return
	}

	// Get user ID from context
	userID, exists := c.Get("user_id")
	if !exists {
		h.logger.Error("User ID not found in context")
		h.errorHandler.HandleAuthenticationError(c, errors.NewAuthenticationError("Authentication context not found"))
		return
	}

	userIDStr, ok := userID.(string)
	if !ok {
		h.logger.Error("Invalid user ID type in context", zap.Any("user_id", userID))
		h.errorHandler.HandleInternalError(c, errors.NewInternalError("Invalid authentication context"))
		return
	}

	// Create the token
	token, err := h.tokenService.CreateStaticToken(c.Request.Context(), &req, userIDStr)
	if err != nil {
		h.logger.Error("Failed to create token", 
			zap.Error(err),
			zap.String("app_id", appID),
			zap.String("user_id", userIDStr))

		// Handle different types of errors appropriately
		if errors.IsNotFound(err) {
			h.errorHandler.HandleError(c, err, "Application not found")
		} else if errors.IsValidationError(err) {
			h.errorHandler.HandleValidationError(c, "token", "Token creation validation failed")
		} else if errors.IsAuthorizationError(err) {
			h.errorHandler.HandleAuthorizationError(c, "token_creation")
		} else {
			h.errorHandler.HandleInternalError(c, err)
		}
		return
	}

	h.logger.Info("Token created successfully", 
		zap.String("token_id", token.ID.String()),
		zap.String("app_id", appID),
		zap.String("user_id", userIDStr))
	
	c.JSON(http.StatusCreated, token)
}

// ListByApp handles GET /applications/:id/tokens
func (h *TokenHandler) ListByApp(c *gin.Context) {
	// Validate application ID parameter
	appID := c.Param("id")
	if appID == "" {
		h.errorHandler.HandleValidationError(c, "id", "Application ID is required")
		return
	}

	// Parse and validate pagination parameters
	limit := 50
	offset := 0

	if l := c.Query("limit"); l != "" {
		if parsed, err := strconv.Atoi(l); err == nil && parsed > 0 && parsed <= 1000 {
			limit = parsed
		} else if parsed <= 0 || parsed > 1000 {
			h.errorHandler.HandleValidationError(c, "limit", "Limit must be between 1 and 1000")
			return
		}
	}

	if o := c.Query("offset"); o != "" {
		if parsed, err := strconv.Atoi(o); err == nil && parsed >= 0 {
			offset = parsed
		} else if parsed < 0 {
			h.errorHandler.HandleValidationError(c, "offset", "Offset must be non-negative")
			return
		}
	}

	// List tokens
	tokens, err := h.tokenService.ListByApp(c.Request.Context(), appID, limit, offset)
	if err != nil {
		h.logger.Error("Failed to list tokens", 
			zap.Error(err), 
			zap.String("app_id", appID),
			zap.Int("limit", limit),
			zap.Int("offset", offset))

		// Handle different types of errors appropriately
		if errors.IsNotFound(err) {
			h.errorHandler.HandleNotFoundError(c, "application", "Application not found")
		} else if errors.IsAuthorizationError(err) {
			h.errorHandler.HandleAuthorizationError(c, "token_list")
		} else {
			h.errorHandler.HandleInternalError(c, err)
		}
		return
	}

	h.logger.Debug("Tokens listed successfully", 
		zap.String("app_id", appID),
		zap.Int("token_count", len(tokens)),
		zap.Int("limit", limit),
		zap.Int("offset", offset))

	c.JSON(http.StatusOK, gin.H{
		"data":   tokens,
		"limit":  limit,
		"offset": offset,
		"count":  len(tokens),
	})
}

// Delete handles DELETE /tokens/:id
func (h *TokenHandler) Delete(c *gin.Context) {
	// Validate token ID parameter
	tokenIDStr := c.Param("id")
	if tokenIDStr == "" {
		h.errorHandler.HandleValidationError(c, "id", "Token ID is required")
		return
	}

	tokenID, err := uuid.Parse(tokenIDStr)
	if err != nil {
		h.logger.Warn("Invalid token ID format", zap.String("token_id", tokenIDStr), zap.Error(err))
		h.errorHandler.HandleValidationError(c, "id", "Invalid token ID format")
		return
	}

	// Get user ID from context
	userID, exists := c.Get("user_id")
	if !exists {
		h.logger.Error("User ID not found in context")
		h.errorHandler.HandleAuthenticationError(c, errors.NewAuthenticationError("Authentication context not found"))
		return
	}

	userIDStr, ok := userID.(string)
	if !ok {
		h.logger.Error("Invalid user ID type in context", zap.Any("user_id", userID))
		h.errorHandler.HandleInternalError(c, errors.NewInternalError("Invalid authentication context"))
		return
	}

	// Delete the token
	err = h.tokenService.Delete(c.Request.Context(), tokenID, userIDStr)
	if err != nil {
		h.logger.Error("Failed to delete token", 
			zap.Error(err), 
			zap.String("token_id", tokenID.String()),
			zap.String("user_id", userIDStr))

		// Handle different types of errors appropriately
		if errors.IsNotFound(err) {
			h.errorHandler.HandleNotFoundError(c, "token", "Token not found")
		} else if errors.IsAuthorizationError(err) {
			h.errorHandler.HandleAuthorizationError(c, "token_deletion")
		} else {
			h.errorHandler.HandleInternalError(c, err)
		}
		return
	}

	h.logger.Info("Token deleted successfully", 
		zap.String("token_id", tokenID.String()),
		zap.String("user_id", userIDStr))
	
	c.JSON(http.StatusNoContent, nil)
}
