package http

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/sandbox0-ai/sandbox0/manager/pkg/service"
	"github.com/sandbox0-ai/sandbox0/pkg/gateway/spec"
	"github.com/sandbox0-ai/sandbox0/pkg/internalauth"
	"go.uber.org/zap"
)

func (s *Server) getExposureDomain() string {
	return service.SandboxAppDomain(s.publicRegionID, s.publicRootDomain)
}

func (s *Server) listSandboxServices(c *gin.Context) {
	sandboxID := c.Param("id")
	if sandboxID == "" {
		spec.JSONError(c, http.StatusBadRequest, spec.CodeBadRequest, "sandbox_id is required")
		return
	}
	claims := internalauth.ClaimsFromContext(c.Request.Context())
	if claims == nil {
		spec.JSONError(c, http.StatusUnauthorized, spec.CodeUnauthorized, "missing authentication")
		return
	}

	sandbox, err := s.sandboxService.GetSandbox(c.Request.Context(), sandboxID)
	if err != nil {
		spec.JSONError(c, http.StatusNotFound, spec.CodeNotFound, fmt.Sprintf("sandbox not found: %v", err))
		return
	}
	if sandbox.TeamID != claims.TeamID {
		spec.JSONError(c, http.StatusForbidden, spec.CodeForbidden, "sandbox belongs to a different team")
		return
	}

	exposureDomain := s.getExposureDomain()
	spec.JSONSuccess(c, http.StatusOK, gin.H{
		"sandbox_id":      sandboxID,
		"services":        service.SandboxAppServiceViewsForExposure(sandboxID, exposureDomain, sandbox.Services),
		"exposure_domain": exposureDomain,
	})
}

func (s *Server) updateSandboxServices(c *gin.Context) {
	sandboxID := c.Param("id")
	if sandboxID == "" {
		spec.JSONError(c, http.StatusBadRequest, spec.CodeBadRequest, "sandbox_id is required")
		return
	}
	claims := internalauth.ClaimsFromContext(c.Request.Context())
	if claims == nil {
		spec.JSONError(c, http.StatusUnauthorized, spec.CodeUnauthorized, "missing authentication")
		return
	}

	var req struct {
		Services *[]service.SandboxAppService `json:"services"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		spec.JSONError(c, http.StatusBadRequest, spec.CodeBadRequest, fmt.Sprintf("invalid request: %v", err))
		return
	}
	if req.Services == nil {
		spec.JSONError(c, http.StatusBadRequest, spec.CodeBadRequest, "services is required")
		return
	}
	services := *req.Services

	sandbox, err := s.sandboxService.GetSandbox(c.Request.Context(), sandboxID)
	if err != nil {
		spec.JSONError(c, http.StatusNotFound, spec.CodeNotFound, fmt.Sprintf("sandbox not found: %v", err))
		return
	}
	if sandbox.TeamID != claims.TeamID {
		spec.JSONError(c, http.StatusForbidden, spec.CodeForbidden, "sandbox belongs to a different team")
		return
	}
	if !sandbox.AutoResume && service.SandboxAppServicesHaveResumeRoute(services) {
		spec.JSONError(c, http.StatusBadRequest, spec.CodeBadRequest,
			"cannot set resume=true on public routes when sandbox auto_resume is disabled")
		return
	}

	updated, err := s.sandboxService.UpdateSandbox(c.Request.Context(), sandboxID, &service.SandboxUpdateConfig{
		Services: services,
	})
	if err != nil {
		s.logger.Error("Failed to update sandbox services",
			zap.String("sandboxID", sandboxID),
			zap.Error(err),
		)
		spec.JSONError(c, http.StatusBadRequest, spec.CodeBadRequest, fmt.Sprintf("failed to update sandbox services: %v", err))
		return
	}

	exposureDomain := s.getExposureDomain()
	spec.JSONSuccess(c, http.StatusOK, gin.H{
		"sandbox_id":      sandboxID,
		"services":        service.SandboxAppServiceViewsForExposure(sandboxID, exposureDomain, updated.Services),
		"exposure_domain": exposureDomain,
	})
}
