package handlers

import (
	"fmt"
	"net/http"
	"time"

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

	apperrors "veza-backend-api/internal/errors"
	"veza-backend-api/internal/services"
	"veza-backend-api/internal/workers"
)

// WebhookHandler gère les handlers de webhooks
type WebhookHandler struct {
	webhookService *services.WebhookService
	webhookWorker  *workers.WebhookWorker
	logger         *zap.Logger
	commonHandler  *CommonHandler
}

// NewWebhookHandler crée un nouveau handler de webhooks
func NewWebhookHandler(
	webhookService *services.WebhookService,
	webhookWorker *workers.WebhookWorker,
	logger *zap.Logger,
) *WebhookHandler {
	return &WebhookHandler{
		webhookService: webhookService,
		webhookWorker:  webhookWorker,
		logger:         logger,
		commonHandler:  NewCommonHandler(logger),
	}
}

// RegisterWebhook gère l'enregistrement d'un webhook
// @Summary      Register webhook
// @Description  Register a new webhook for receiving events
// @Tags         Webhook
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        webhook  body      object  true  "Webhook registration data" SchemaExample({"url": "https://example.com/webhook", "events": ["track.uploaded", "playlist.created"]})
// @Success      201  {object}  handlers.APIResponse{data=object{webhook=object}}
// @Failure      400  {object}  handlers.APIResponse "Validation error"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /webhooks [post]
func (h *WebhookHandler) RegisterWebhook() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInvalidCredentials, "User not authenticated"))
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type"))
			return
		}

		var req struct {
			URL    string   `json:"url" binding:"required,url"`
			Events []string `json:"events" binding:"required,min=1"`
		}

		if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
			RespondWithAppError(c, appErr)
			return
		}

		webhook, err := h.webhookService.RegisterWebhook(c.Request.Context(), userID, req.URL, req.Events)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to register webhook", err))
			return
		}

		RespondSuccess(c, http.StatusCreated, webhook)
	}
}

// ListWebhooks liste les webhooks d'un utilisateur
// @Summary      List webhooks
// @Description  Get a list of all webhooks registered by the current user
// @Tags         Webhook
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200  {object}  handlers.APIResponse{data=object{webhooks=array}}
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /webhooks [get]
func (h *WebhookHandler) ListWebhooks() gin.HandlerFunc {
	return func(c *gin.Context) {
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInvalidCredentials, "User not authenticated"))
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type"))
			return
		}

		webhooks, err := h.webhookService.ListWebhooks(c.Request.Context(), userID)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to list webhooks", err))
			return
		}

		RespondSuccess(c, http.StatusOK, webhooks)
	}
}

// DeleteWebhook supprime un webhook
// @Summary      Delete webhook
// @Description  Delete a webhook by ID
// @Tags         Webhook
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "Webhook ID"
// @Success      200  {object}  handlers.APIResponse{data=object{message=string}}
// @Failure      400  {object}  handlers.APIResponse "Invalid webhook ID"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      404  {object}  handlers.APIResponse "Webhook not found"
// @Router       /webhooks/{id} [delete]
func (h *WebhookHandler) DeleteWebhook() gin.HandlerFunc {
	return func(c *gin.Context) {
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInvalidCredentials, "User not authenticated"))
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type"))
			return
		}

		webhookIDStr := c.Param("id")
		webhookID, err := uuid.Parse(webhookIDStr)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "Invalid webhook ID"))
			return
		}

		err = h.webhookService.DeleteWebhook(c.Request.Context(), webhookID, userID)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeNotFound, "Webhook not found"))
			return
		}

		RespondSuccess(c, http.StatusOK, gin.H{"message": "Webhook deleted successfully"})
	}
}

// GetWebhookStats retourne les statistiques des webhooks
// @Summary      Get webhook statistics
// @Description  Get statistics for webhook delivery and performance
// @Tags         Webhook
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200  {object}  handlers.APIResponse{data=object{stats=object}}
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /webhooks/stats [get]
// BE-API-033: Implement webhook stats endpoint validation
func (h *WebhookHandler) GetWebhookStats() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Récupérer l'ID utilisateur depuis le contexte (pour cohérence avec les autres endpoints protégés)
		userID, ok := GetUserIDUUID(c)
		if !ok {
			return // Erreur déjà envoyée par GetUserIDUUID
		}

		// Récupérer les statistiques du worker
		if h.webhookWorker == nil {
			h.logger.Error("WebhookWorker not available")
			RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Webhook stats service not available", nil))
			return
		}

		stats := h.webhookWorker.GetStats()

		// BE-API-033: Standardize response format
		RespondSuccess(c, http.StatusOK, gin.H{
			"user_id": userID,
			"stats":   stats,
		})
	}
}

// TestWebhook teste un webhook
// @Summary      Test webhook
// @Description  Send a test event to a webhook to verify it's working
// @Tags         Webhook
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "Webhook ID"
// @Success      200  {object}  handlers.APIResponse{data=object{message=string}}
// @Failure      400  {object}  handlers.APIResponse "Invalid webhook ID"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      404  {object}  handlers.APIResponse "Webhook not found"
// @Router       /webhooks/{id}/test [post]
func (h *WebhookHandler) TestWebhook() gin.HandlerFunc {
	return func(c *gin.Context) {
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInvalidCredentials, "User not authenticated"))
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type"))
			return
		}

		webhookIDStr := c.Param("id")
		webhookID, err := uuid.Parse(webhookIDStr)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "Invalid webhook ID"))
			return
		}

		webhook, err := h.webhookService.GetWebhook(c.Request.Context(), webhookID, userID)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeNotFound, "Webhook not found"))
			return
		}

		// INT-008: Standardize date format to ISO 8601 (RFC3339)
		job := workers.WebhookJob{
			Webhook: webhook,
			Event:   "ping",
			Data: map[string]interface{}{
				"message":   "This is a test webhook from Veza",
				"timestamp": time.Now().UTC().Format(time.RFC3339),
				"test_id":   uuid.New().String(),
			},
			Retries: 0,
		}

		h.webhookWorker.Enqueue(job)

		h.logger.Info("Test webhook queued", zap.String("webhook_id", webhookID.String()))

		RespondSuccess(c, http.StatusOK, gin.H{"message": fmt.Sprintf("Webhook test queued for %s", webhookID)})
	}
}

// RegenerateAPIKey régénère la clé API d'un webhook
// @Summary      Regenerate webhook API key
// @Description  Generate a new API key for a webhook (invalidates the old one)
// @Tags         Webhook
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      string  true  "Webhook ID"
// @Success      200  {object}  handlers.APIResponse{data=object{api_key=string,message=string}}
// @Failure      400  {object}  handlers.APIResponse "Invalid webhook ID"
// @Failure      401  {object}  handlers.APIResponse "Unauthorized"
// @Failure      404  {object}  handlers.APIResponse "Webhook not found"
// @Failure      500  {object}  handlers.APIResponse "Internal server error"
// @Router       /webhooks/{id}/regenerate-key [post]
// BE-SEC-012: Regenerate webhook API key
func (h *WebhookHandler) RegenerateAPIKey() gin.HandlerFunc {
	return func(c *gin.Context) {
		userIDInterface, exists := c.Get("user_id")
		if !exists {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInvalidCredentials, "User not authenticated"))
			return
		}

		userID, ok := userIDInterface.(uuid.UUID)
		if !ok {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeInternal, "Invalid user ID type"))
			return
		}

		webhookIDStr := c.Param("id")
		webhookID, err := uuid.Parse(webhookIDStr)
		if err != nil {
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.New(apperrors.ErrCodeValidation, "Invalid webhook ID"))
			return
		}

		newAPIKey, err := h.webhookService.RegenerateAPIKey(c.Request.Context(), webhookID, userID)
		if err != nil {
			if err.Error() == "webhook not found" {
				// INT-006: Standardize error response format
				RespondWithAppError(c, apperrors.New(apperrors.ErrCodeNotFound, "Webhook not found"))
				return
			}
			// INT-006: Standardize error response format
			RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to regenerate API key", err))
			return
		}

		RespondSuccess(c, http.StatusOK, gin.H{
			"api_key": newAPIKey,
			"message": "API key regenerated successfully",
		})
	}
}
