package handlers

import (
	"fmt"
	"net/http"

	"git.linuxforward.com/byop/byop-engine/dbstore"
	"git.linuxforward.com/byop/byop-engine/models"
	"github.com/gin-gonic/gin"
)

// DeploymentHandler handles deployment-related operations and contains integrated service logic
type DeploymentHandler struct {
	store *dbstore.SQLiteStore
}

// NewDeploymentHandler creates a new DeploymentHandler
func NewDeploymentHandler(store *dbstore.SQLiteStore) *DeploymentHandler {
	return &DeploymentHandler{
		store: store,
	}
}

// ListDeployments returns all deployments with optional filtering
func (h *DeploymentHandler) ListDeployments(c *gin.Context) {
	filter := make(map[string]interface{})
	ctx := c.Request.Context()

	// Attempt to bind query parameters, but allow empty filters
	if err := c.ShouldBindQuery(&filter); err != nil && len(filter) > 0 {
		appErr := models.NewErrValidation("invalid_query_params", nil, err)
		models.RespondWithError(c, appErr)
		return
	}

	deployments, err := h.store.GetAllDeployments(ctx)
	if err != nil {
		appErr := models.NewErrInternalServer("failed_list_deployments", fmt.Errorf("Failed to list deployments: %w", err))
		models.RespondWithError(c, appErr)
		return
	}

	// If empty, return an empty list
	if len(deployments) == 0 {
		c.JSON(http.StatusOK, []models.Deployment{})
		return
	}

	c.JSON(http.StatusOK, deployments)
}

// CreateDeployment creates a new deployment
func (h *DeploymentHandler) CreateDeployment(c *gin.Context) {
	var deployment models.Deployment
	ctx := c.Request.Context()

	if err := c.ShouldBindJSON(&deployment); err != nil {
		appErr := models.NewErrValidation("invalid_request_body", nil, err)
		models.RespondWithError(c, appErr)
		return
	}

	// Basic validation
	validationErrors := make(map[string]string)
	if deployment.AppID == 0 {
		validationErrors["app_id"] = "App ID is required"
	}
	if deployment.Environment == "" {
		validationErrors["environment"] = "Environment is required"
	}
	if len(validationErrors) > 0 {
		appErr := models.NewErrValidation("deployment_validation_failed", validationErrors, nil)
		models.RespondWithError(c, appErr)
		return
	}

	// Set default status
	if deployment.Status == "" {
		deployment.Status = "pending"
	}

	// TODO: Add complex deployment logic with cloud providers
	err := h.store.CreateDeployment(ctx, &deployment)
	if err != nil {
		appErr := models.NewErrInternalServer("failed_create_deployment", fmt.Errorf("Failed to create deployment: %w", err))
		models.RespondWithError(c, appErr)
		return
	}

	// GORM automatically sets the ID after creation

	c.JSON(http.StatusCreated, deployment)
}

// GetDeployment returns a specific deployment
func (h *DeploymentHandler) GetDeployment(c *gin.Context) {
	ctx := c.Request.Context()

	id, err := parseUintID(c, "id")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	deployment, err := h.store.GetDeploymentByID(ctx, id)
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	c.JSON(http.StatusOK, deployment)
}

// UpdateDeployment updates a deployment
func (h *DeploymentHandler) UpdateDeployment(c *gin.Context) {
	ctx := c.Request.Context()

	id, err := parseUintID(c, "id")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	var updatedDeployment models.Deployment
	if err := c.ShouldBindJSON(&updatedDeployment); err != nil {
		appErr := models.NewErrValidation("invalid_request_body", nil, err)
		models.RespondWithError(c, appErr)
		return
	}

	// Ensure the ID matches the URL parameter
	updatedDeployment.ID = id

	// Basic validation for update
	validationErrors := make(map[string]string)
	if updatedDeployment.AppID == 0 {
		validationErrors["app_id"] = "App ID is required"
	}
	if updatedDeployment.Environment == "" {
		validationErrors["environment"] = "Environment is required"
	}
	if len(validationErrors) > 0 {
		appErr := models.NewErrValidation("deployment_update_validation_failed", validationErrors, nil)
		models.RespondWithError(c, appErr)
		return
	}

	if err := h.store.UpdateDeployment(ctx, &updatedDeployment); err != nil {
		models.RespondWithError(c, err)
		return
	}

	c.JSON(http.StatusOK, updatedDeployment)
}

// DeleteDeployment deletes a deployment
func (h *DeploymentHandler) DeleteDeployment(c *gin.Context) {
	ctx := c.Request.Context()

	id, err := parseUintID(c, "id")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	if err := h.store.DeleteDeployment(ctx, id); err != nil {
		models.RespondWithError(c, err)
		return
	}

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

// UpdateDeploymentStatus updates the status of a deployment
func (h *DeploymentHandler) UpdateDeploymentStatus(c *gin.Context) {
	ctx := c.Request.Context()

	id, err := parseUintID(c, "id")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	var statusUpdate struct {
		Status string `json:"status" binding:"required"`
	}

	if err := c.ShouldBindJSON(&statusUpdate); err != nil {
		appErr := models.NewErrValidation("invalid_status_update_body", nil, err)
		models.RespondWithError(c, appErr)
		return
	}

	// Get current deployment
	deployment, err := h.store.GetDeploymentByID(ctx, id)
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	// Update the status
	deployment.Status = statusUpdate.Status
	if err := h.store.UpdateDeployment(ctx, deployment); err != nil {
		if _, ok := err.(models.CustomError); !ok {
			err = models.NewErrInternalServer("failed_update_deployment_status", fmt.Errorf("Failed to update deployment status: %w", err))
		}
		models.RespondWithError(c, err)
		return
	}

	c.Status(http.StatusOK)
}

// GetDeploymentsByClient returns all deployments for a specific client
func (h *DeploymentHandler) GetDeploymentsByClient(c *gin.Context) {
	ctx := c.Request.Context()

	clientID, err := parseUintID(c, "clientId")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	deployments, err := h.store.GetDeploymentsByClientID(ctx, clientID)
	if err != nil {
		if _, ok := err.(models.CustomError); !ok {
			err = models.NewErrInternalServer("failed_fetch_deployments_by_client", fmt.Errorf("Failed to fetch deployments for client %d: %w", clientID, err))
		}
		models.RespondWithError(c, err)
		return
	}

	c.JSON(http.StatusOK, deployments)
}

// GetDeploymentsByApp returns all deployments for a specific app
func (h *DeploymentHandler) GetDeploymentsByApp(c *gin.Context) {
	ctx := c.Request.Context()

	appID, err := parseUintID(c, "appId")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	deployments, err := h.store.GetDeploymentsByAppID(ctx, appID)
	if err != nil {
		if _, ok := err.(models.CustomError); !ok {
			err = models.NewErrInternalServer("failed_fetch_deployments_by_app", fmt.Errorf("Failed to fetch deployments for app %d: %w", appID, err))
		}
		models.RespondWithError(c, err)
		return
	}

	c.JSON(http.StatusOK, deployments)
}

// GetDeploymentsByUser returns all deployments created by a specific user (via their apps)
func (h *DeploymentHandler) GetDeploymentsByUser(c *gin.Context) {
	ctx := c.Request.Context()

	userID, err := parseUintID(c, "userId")
	if err != nil {
		models.RespondWithError(c, err)
		return
	}

	deployments, err := h.store.GetDeploymentsByUserID(ctx, userID)
	if err != nil {
		if _, ok := err.(models.CustomError); !ok {
			err = models.NewErrInternalServer("failed_fetch_deployments_by_user", fmt.Errorf("Failed to fetch deployments for user %d: %w", userID, err))
		}
		models.RespondWithError(c, err)
		return
	}

	c.JSON(http.StatusOK, deployments)
}
