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 FunctionHandler struct {
	functionService services.FunctionService
	authService     services.AuthService
	logger          *zap.Logger
}

func NewFunctionHandler(functionService services.FunctionService, authService services.AuthService, logger *zap.Logger) *FunctionHandler {
	return &FunctionHandler{
		functionService: functionService,
		authService:     authService,
		logger:          logger,
	}
}

func (h *FunctionHandler) Create(c *gin.Context) {
	var req domain.CreateFunctionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Warn("Invalid create function request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
		return
	}

	// Auto-select image based on runtime if not provided or empty
	if req.Image == "" {
		if runtimeConfig, exists := domain.GetRuntimeConfig(req.Runtime); exists && runtimeConfig.Image != "" {
			req.Image = runtimeConfig.Image
		}
	}

	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.write") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

	function, err := h.functionService.Create(c.Request.Context(), &req, authCtx.UserID)
	if err != nil {
		h.logger.Error("Failed to create function", zap.Error(err))
		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
		return
	}

	h.logger.Info("Function created successfully", 
		zap.String("function_id", function.ID.String()),
		zap.String("name", function.Name),
		zap.String("user_id", authCtx.UserID))

	c.JSON(http.StatusCreated, function)
}

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

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

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

	c.JSON(http.StatusOK, function)
}

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

	var req domain.UpdateFunctionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		h.logger.Warn("Invalid update function request", zap.Error(err))
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format"})
		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.write") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

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

	h.logger.Info("Function updated successfully", 
		zap.String("function_id", id.String()),
		zap.String("user_id", authCtx.UserID))

	c.JSON(http.StatusOK, function)
}

func (h *FunctionHandler) Delete(c *gin.Context) {
	idStr := c.Param("id")
	id, err := uuid.Parse(idStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid function 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.delete") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

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

	h.logger.Info("Function deleted successfully", 
		zap.String("function_id", id.String()),
		zap.String("user_id", authCtx.UserID))

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

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

	appID := c.Query("app_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
	}

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

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

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

	var req domain.DeployFunctionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		// Allow empty body for deploy
		req = domain.DeployFunctionRequest{
			FunctionID: id,
			Force:      false,
		}
	}
	req.FunctionID = id

	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.deploy") {
		c.JSON(http.StatusForbidden, gin.H{"error": "Insufficient permissions"})
		return
	}

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

	h.logger.Info("Function deployed successfully", 
		zap.String("function_id", id.String()),
		zap.String("user_id", authCtx.UserID))

	c.JSON(http.StatusOK, response)
}