package handlers

import (
	"net/http"
	"time"

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

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

// AuditHandler handles audit-related HTTP requests
type AuditHandler struct {
	auditLogger   audit.AuditLogger
	authService   services.AuthenticationService
	validator     *validation.Validator
	errorHandler  *errors.ErrorHandler
	logger        *zap.Logger
}

// NewAuditHandler creates a new audit handler
func NewAuditHandler(
	auditLogger audit.AuditLogger,
	authService services.AuthenticationService,
	logger *zap.Logger,
) *AuditHandler {
	return &AuditHandler{
		auditLogger:  auditLogger,
		authService:  authService,
		validator:    validation.NewValidator(logger),
		errorHandler: errors.NewErrorHandler(logger),
		logger:       logger,
	}
}

// AuditQueryRequest represents the request for querying audit events
type AuditQueryRequest struct {
	EventTypes   []string  `json:"event_types,omitempty" form:"event_types"`
	Statuses     []string  `json:"statuses,omitempty" form:"statuses"`
	ActorID      string    `json:"actor_id,omitempty" form:"actor_id"`
	ResourceID   string    `json:"resource_id,omitempty" form:"resource_id"`
	ResourceType string    `json:"resource_type,omitempty" form:"resource_type"`
	StartTime    *string   `json:"start_time,omitempty" form:"start_time"`
	EndTime      *string   `json:"end_time,omitempty" form:"end_time"`
	Limit        int       `json:"limit,omitempty" form:"limit"`
	Offset       int       `json:"offset,omitempty" form:"offset"`
	OrderBy      string    `json:"order_by,omitempty" form:"order_by"`
	OrderDesc    *bool     `json:"order_desc,omitempty" form:"order_desc"`
}

// AuditStatsRequest represents the request for audit statistics
type AuditStatsRequest struct {
	EventTypes []string `json:"event_types,omitempty" form:"event_types"`
	StartTime  *string  `json:"start_time,omitempty" form:"start_time"`
	EndTime    *string  `json:"end_time,omitempty" form:"end_time"`
	GroupBy    string   `json:"group_by,omitempty" form:"group_by"`
}

// AuditResponse represents the response structure for audit queries
type AuditResponse struct {
	Events []AuditEventResponse `json:"events"`
	Total  int                  `json:"total"`
	Limit  int                  `json:"limit"`
	Offset int                  `json:"offset"`
}

// AuditEventResponse represents a single audit event in API responses
type AuditEventResponse struct {
	ID           string                 `json:"id"`
	Type         string                 `json:"type"`
	Status       string                 `json:"status"`
	Timestamp    string                 `json:"timestamp"`
	ActorID      string                 `json:"actor_id,omitempty"`
	ActorIP      string                 `json:"actor_ip,omitempty"`
	UserAgent    string                 `json:"user_agent,omitempty"`
	ResourceID   string                 `json:"resource_id,omitempty"`
	ResourceType string                 `json:"resource_type,omitempty"`
	Action       string                 `json:"action"`
	Description  string                 `json:"description"`
	Details      map[string]interface{} `json:"details,omitempty"`
	RequestID    string                 `json:"request_id,omitempty"`
	SessionID    string                 `json:"session_id,omitempty"`
}

// ListEvents handles GET /audit/events
func (h *AuditHandler) ListEvents(c *gin.Context) {
	// Parse query parameters
	var req AuditQueryRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		h.errorHandler.HandleValidationError(c, "query_params", "Invalid query parameters")
		return
	}

	// Set defaults
	if req.Limit <= 0 || req.Limit > 1000 {
		req.Limit = 100
	}
	if req.Offset < 0 {
		req.Offset = 0
	}
	if req.OrderBy == "" {
		req.OrderBy = "timestamp"
	}
	if req.OrderDesc == nil {
		orderDesc := true
		req.OrderDesc = &orderDesc
	}

	// Convert request to audit filter
	filter := &audit.AuditFilter{
		ActorID:      req.ActorID,
		ResourceID:   req.ResourceID,
		ResourceType: req.ResourceType,
		Limit:        req.Limit,
		Offset:       req.Offset,
		OrderBy:      req.OrderBy,
		OrderDesc:    *req.OrderDesc,
	}

	// Convert event types
	for _, et := range req.EventTypes {
		filter.EventTypes = append(filter.EventTypes, audit.EventType(et))
	}

	// Convert statuses
	for _, st := range req.Statuses {
		filter.Statuses = append(filter.Statuses, audit.EventStatus(st))
	}

	// Parse time filters
	if req.StartTime != nil && *req.StartTime != "" {
		if startTime, err := time.Parse(time.RFC3339, *req.StartTime); err == nil {
			filter.StartTime = &startTime
		} else {
			h.errorHandler.HandleValidationError(c, "start_time", "Invalid start_time format, use RFC3339")
			return
		}
	}

	if req.EndTime != nil && *req.EndTime != "" {
		if endTime, err := time.Parse(time.RFC3339, *req.EndTime); err == nil {
			filter.EndTime = &endTime
		} else {
			h.errorHandler.HandleValidationError(c, "end_time", "Invalid end_time format, use RFC3339")
			return
		}
	}

	// Query audit events
	events, err := h.auditLogger.QueryEvents(c.Request.Context(), filter)
	if err != nil {
		h.logger.Error("Failed to query audit events", zap.Error(err))
		h.errorHandler.HandleInternalError(c, err)
		return
	}

	// Convert to response format
	response := &AuditResponse{
		Events: make([]AuditEventResponse, len(events)),
		Total:  len(events), // Note: This is just the count of returned events, not total matching
		Limit:  req.Limit,
		Offset: req.Offset,
	}

	for i, event := range events {
		response.Events[i] = AuditEventResponse{
			ID:           event.ID.String(),
			Type:         string(event.Type),
			Status:       string(event.Status),
			Timestamp:    event.Timestamp.Format(time.RFC3339),
			ActorID:      event.ActorID,
			ActorIP:      event.ActorIP,
			UserAgent:    event.UserAgent,
			ResourceID:   event.ResourceID,
			ResourceType: event.ResourceType,
			Action:       event.Action,
			Description:  event.Description,
			Details:      event.Details,
			RequestID:    event.RequestID,
			SessionID:    event.SessionID,
		}
	}

	c.JSON(http.StatusOK, response)
}

// GetEvent handles GET /audit/events/:id
func (h *AuditHandler) GetEvent(c *gin.Context) {
	eventIDStr := c.Param("id")
	eventID, err := uuid.Parse(eventIDStr)
	if err != nil {
		h.errorHandler.HandleValidationError(c, "id", "Invalid event ID format")
		return
	}

	// Get the specific audit event
	event, err := h.auditLogger.GetEventByID(c.Request.Context(), eventID)
	if err != nil {
		h.logger.Error("Failed to get audit event", zap.Error(err), zap.String("event_id", eventID.String()))
		// Check if it's a not found error
		if err.Error() == "audit event with ID '"+eventID.String()+"' not found" {
			h.errorHandler.HandleNotFoundError(c, "audit_event", "Audit event not found")
		} else {
			h.errorHandler.HandleInternalError(c, err)
		}
		return
	}

	// Convert to response format
	response := AuditEventResponse{
		ID:           event.ID.String(),
		Type:         string(event.Type),
		Status:       string(event.Status),
		Timestamp:    event.Timestamp.Format(time.RFC3339),
		ActorID:      event.ActorID,
		ActorIP:      event.ActorIP,
		UserAgent:    event.UserAgent,
		ResourceID:   event.ResourceID,
		ResourceType: event.ResourceType,
		Action:       event.Action,
		Description:  event.Description,
		Details:      event.Details,
		RequestID:    event.RequestID,
		SessionID:    event.SessionID,
	}

	c.JSON(http.StatusOK, response)
}

// GetStats handles GET /audit/stats  
func (h *AuditHandler) GetStats(c *gin.Context) {
	// Parse query parameters
	var req AuditStatsRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		h.errorHandler.HandleValidationError(c, "query_params", "Invalid query parameters")
		return
	}

	// Convert request to audit stats filter
	filter := &audit.AuditStatsFilter{
		GroupBy: req.GroupBy,
	}

	// Convert event types
	for _, et := range req.EventTypes {
		filter.EventTypes = append(filter.EventTypes, audit.EventType(et))
	}

	// Parse time filters
	if req.StartTime != nil && *req.StartTime != "" {
		if startTime, err := time.Parse(time.RFC3339, *req.StartTime); err == nil {
			filter.StartTime = &startTime
		} else {
			h.errorHandler.HandleValidationError(c, "start_time", "Invalid start_time format, use RFC3339")
			return
		}
	}

	if req.EndTime != nil && *req.EndTime != "" {
		if endTime, err := time.Parse(time.RFC3339, *req.EndTime); err == nil {
			filter.EndTime = &endTime
		} else {
			h.errorHandler.HandleValidationError(c, "end_time", "Invalid end_time format, use RFC3339")
			return
		}
	}

	// Get audit statistics
	stats, err := h.auditLogger.GetEventStats(c.Request.Context(), filter)
	if err != nil {
		h.logger.Error("Failed to get audit statistics", zap.Error(err))
		h.errorHandler.HandleInternalError(c, err)
		return
	}

	c.JSON(http.StatusOK, stats)
}