package handlers

import (
	"net/http"
	"strconv"

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

	"github.com/RyanCopley/skybridge/faas/internal/domain"
	"github.com/RyanCopley/skybridge/faas/internal/services"
)

type ExecutionHandler struct {
	executionService services.ExecutionService
	authService      services.AuthService
	logger           *zap.Logger
}

func NewExecutionHandler(executionService services.ExecutionService, authService services.AuthService, logger *zap.Logger) *ExecutionHandler {
	return &ExecutionHandler{
		executionService: executionService,
		authService:      authService,
		logger:           logger,
	}
}

func (h *ExecutionHandler) Execute(c *gin.Context) {
	idStr := c.Param("id")
	functionID, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function ID"})
		return
	}

	var req domain.ExecuteFunctionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Warn("Invalid execute function request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
		return
	}
	req.FunctionID = functionID

	authCtx, err := h.authService.GetAuthContext(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get auth context", zap.Error(err))
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	if !h.authService.HasPermission(c.Request.Context(), "faas.execute") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	response, err := h.executionService.Execute(c.Request.Context(), &req, authCtx.UserID)
	if err != nil {
		h.logger.Error("Failed to execute function", zap.String("function_id", idStr), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	h.logger.Info("Function execution initiated", 
		zap.String("function_id", functionID.String()),
		zap.String("execution_id", response.ExecutionID.String()),
		zap.String("user_id", authCtx.UserID),
		zap.Bool("async", req.Async))

	c.JSON(http.StatusOK, response)
}

func (h *ExecutionHandler) Invoke(c *gin.Context) {
	idStr := c.Param("id")
	functionID, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function ID"})
		return
	}

	var req domain.ExecuteFunctionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		// Allow empty body
		req = domain.ExecuteFunctionRequest{
			FunctionID: functionID,
			Async:      true,
		}
	}
	req.FunctionID = functionID
	req.Async = true // Force async for invoke endpoint

	authCtx, err := h.authService.GetAuthContext(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get auth context", zap.Error(err))
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	if !h.authService.HasPermission(c.Request.Context(), "faas.execute") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	response, err := h.executionService.Execute(c.Request.Context(), &req, authCtx.UserID)
	if err != nil {
		h.logger.Error("Failed to invoke function", zap.String("function_id", idStr), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	h.logger.Info("Function invoked successfully", 
		zap.String("function_id", functionID.String()),
		zap.String("execution_id", response.ExecutionID.String()),
		zap.String("user_id", authCtx.UserID))

	c.JSON(http.StatusAccepted, response)
}

func (h *ExecutionHandler) GetByID(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid execution ID"})
		return
	}

	if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	execution, err := h.executionService.GetByID(c.Request.Context(), id)
	if err != nil {
		h.logger.Error("Failed to get execution", zap.String("id", idStr), zap.Error(err))
		c.JSON(http.StatusNotFound, gin.H{"error": "Execution not found"})
		return
	}

	c.JSON(http.StatusOK, execution)
}

func (h *ExecutionHandler) List(c *gin.Context) {
	if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	var functionID *uuid.UUID
	functionIDStr := c.Query("function_id")
	if functionIDStr != "" {
		id, err := uuid.Parse(functionIDStr)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function ID"})
			return
		}
		functionID = &id
	}

	limitStr := c.DefaultQuery("limit", "50")
	offsetStr := c.DefaultQuery("offset", "0")

	limit, err := strconv.Atoi(limitStr)
	if err != nil || limit <= 0 {
		limit = 50
	}

	offset, err := strconv.Atoi(offsetStr)
	if err != nil || offset < 0 {
		offset = 0
	}

	executions, err := h.executionService.List(c.Request.Context(), functionID, limit, offset)
	if err != nil {
		h.logger.Error("Failed to list executions", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"executions": executions,
		"limit":      limit,
		"offset":     offset,
	})
}

func (h *ExecutionHandler) Cancel(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid execution ID"})
		return
	}

	authCtx, err := h.authService.GetAuthContext(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get auth context", zap.Error(err))
		c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication required"})
		return
	}

	if !h.authService.HasPermission(c.Request.Context(), "faas.execute") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	if err := h.executionService.Cancel(c.Request.Context(), id, authCtx.UserID); err != nil {
		h.logger.Error("Failed to cancel execution", zap.String("id", idStr), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	h.logger.Info("Execution canceled successfully", 
		zap.String("execution_id", id.String()),
		zap.String("user_id", authCtx.UserID))

	c.JSON(http.StatusOK, gin.H{"message": "Execution canceled successfully"})
}

func (h *ExecutionHandler) GetLogs(c *gin.Context) {
	idStr := c.Param("id")
	h.logger.Debug("GetLogs endpoint called",
		zap.String("execution_id", idStr),
		zap.String("client_ip", c.ClientIP()))
		
	id, err := uuid.Parse(idStr)
	if err != nil {
		h.logger.Warn("Invalid execution ID provided to GetLogs",
			zap.String("id", idStr),
			zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid execution ID"})
		return
	}

	if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
		h.logger.Warn("Insufficient permissions for GetLogs",
			zap.String("execution_id", idStr))
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	h.logger.Debug("Calling execution service GetLogs",
		zap.String("execution_id", idStr))
		
	logs, err := h.executionService.GetLogs(c.Request.Context(), id)
	if err != nil {
		h.logger.Error("Failed to get execution logs", zap.String("id", idStr), zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	h.logger.Debug("Successfully retrieved logs from execution service",
		zap.String("execution_id", idStr),
		zap.Int("log_count", len(logs)))

	c.JSON(http.StatusOK, gin.H{
		"logs": logs,
	})
}

func (h *ExecutionHandler) GetRunning(c *gin.Context) {
	if !h.authService.HasPermission(c.Request.Context(), "faas.read") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	executions, err := h.executionService.GetRunningExecutions(c.Request.Context())
	if err != nil {
		h.logger.Error("Failed to get running executions", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"executions": executions,
		"count":      len(executions),
	})
}