// Package handlers provides HTTP handlers for the rdev API.
package handlers

import (
	"context"
	"errors"
	"net/http"

	"github.com/orchard9/rdev/internal/auth"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/logging"
	"github.com/orchard9/rdev/internal/service"
	"github.com/orchard9/rdev/internal/validate"
	"github.com/orchard9/rdev/pkg/api"
)

// CreateAndBuildHandler handles the combined create-project-and-build endpoint.
type CreateAndBuildHandler struct {
	infraService *service.ProjectInfraService
	buildService *service.BuildService
}

// NewCreateAndBuildHandler creates a new create-and-build handler.
func NewCreateAndBuildHandler(
	infraService *service.ProjectInfraService,
	buildService *service.BuildService,
) *CreateAndBuildHandler {
	return &CreateAndBuildHandler{
		infraService: infraService,
		buildService: buildService,
	}
}

// Mount registers the create-and-build route.
func (h *CreateAndBuildHandler) Mount(r api.Router) {
	// Requires both project execute (create) and build write (start build)
	r.With(auth.RequireScope(auth.ScopeBuildWrite, auth.ScopeAdmin)).
		Post("/project/create-and-build", h.CreateAndBuild)
}

// CreateAndBuildRequest is the request body for POST /project/create-and-build.
type CreateAndBuildRequest struct {
	// Project creation fields
	Name        string `json:"name"`
	Description string `json:"description,omitempty"`
	Private     bool   `json:"private,omitempty"`
	Template    string `json:"template,omitempty"`

	// Build fields
	Prompt      string            `json:"prompt"`
	Variables   map[string]string `json:"variables,omitempty"`
	AutoCommit  bool              `json:"auto_commit"`
	AutoPush    bool              `json:"auto_push"`
	CallbackURL string            `json:"callback_url,omitempty"`
}

// CreateAndBuildResponse is the response for POST /project/create-and-build.
type CreateAndBuildResponse struct {
	// Project info
	ProjectID string `json:"project_id"`
	Name      string `json:"name"`
	Domain    string `json:"domain"`
	URL       string `json:"url"`

	// Git info
	Git map[string]string `json:"git,omitempty"`

	// Build info
	TaskID    string `json:"task_id"`
	Status    string `json:"status"`
	StatusURL string `json:"status_url"`
}

// CreateAndBuild creates a project and immediately enqueues a build task.
// POST /project/create-and-build
func (h *CreateAndBuildHandler) CreateAndBuild(w http.ResponseWriter, r *http.Request) {
	ctx, cancel := context.WithTimeout(r.Context(), TimeoutHeavyWrite)
	defer cancel()

	if h.infraService == nil {
		api.WriteInternalError(w, r, "project infrastructure service not configured")
		return
	}
	if h.buildService == nil {
		api.WriteInternalError(w, r, "build service not configured")
		return
	}

	r.Body = http.MaxBytesReader(w, r.Body, maxRequestBodySize)
	var req CreateAndBuildRequest
	if err := api.DecodeJSON(r, &req); err != nil {
		api.WriteBadRequest(w, r, "invalid request body")
		return
	}

	v := validate.New()
	v.Required(req.Name, "name")
	v.Required(req.Prompt, "prompt")
	if err := v.Error(); err != nil {
		api.WriteBadRequest(w, r, err.Error())
		return
	}

	// Validate callback URL to prevent SSRF
	if req.CallbackURL != "" {
		if err := domain.ValidateCallbackURL(req.CallbackURL); err != nil {
			api.WriteBadRequest(w, r, err.Error())
			return
		}
	}

	// Step 1: Create the project
	projectResult, err := h.infraService.CreateProject(ctx, service.CreateProjectRequest{
		Name:        req.Name,
		Description: req.Description,
		Private:     req.Private,
		Template:    req.Template,
	})
	if err != nil {
		if errors.Is(err, domain.ErrInvalidProjectName) {
			api.WriteBadRequest(w, r, err.Error())
			return
		}
		log := logging.FromContext(ctx).WithHandler("CreateAndBuild")
		log.Error("project creation failed", logging.FieldError, err.Error(), logging.FieldProjectName, req.Name)
		api.WriteInternalError(w, r, "failed to create project")
		return
	}

	// Step 2: Enqueue the build task
	spec := domain.BuildSpec{
		Prompt:      req.Prompt,
		Template:    req.Template,
		Variables:   req.Variables,
		AutoCommit:  req.AutoCommit,
		AutoPush:    req.AutoPush,
		CallbackURL: req.CallbackURL,
		GitCloneURL: projectResult.CloneHTTP, // Required for git ops on shared worker pods
	}

	taskID, err := h.buildService.StartBuild(ctx, projectResult.ProjectID, spec)
	if err != nil {
		log := logging.FromContext(ctx).WithHandler("CreateAndBuild")
		log.Error("build enqueue failed after project creation",
			logging.FieldError, err.Error(),
			logging.FieldProjectID, projectResult.ProjectID,
		)
		// Project was created but build failed to enqueue.
		// Return the project info with a generic error and retry URL.
		api.WriteJSON(w, r, http.StatusCreated, map[string]any{
			"project_id":  projectResult.ProjectID,
			"name":        projectResult.Name,
			"domain":      projectResult.Domain,
			"url":         projectResult.URL,
			"build_error": "project created but build failed to enqueue",
			"retry_url":   "/projects/" + projectResult.ProjectID + "/builds",
		})
		return
	}

	resp := CreateAndBuildResponse{
		ProjectID: projectResult.ProjectID,
		Name:      projectResult.Name,
		Domain:    projectResult.Domain,
		URL:       projectResult.URL,
		TaskID:    taskID,
		Status:    "pending",
		StatusURL: "/builds/" + taskID,
	}

	if projectResult.CloneHTTP != "" {
		resp.Git = map[string]string{
			"owner":      projectResult.GitRepoOwner,
			"name":       projectResult.GitRepoName,
			"clone_ssh":  projectResult.CloneSSH,
			"clone_http": projectResult.CloneHTTP,
			"html_url":   projectResult.HTMLURL,
		}
	}

	api.WriteCreated(w, r, resp)
}
