package handlers

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

	"github.com/go-chi/chi/v5"
	"github.com/orchard9/rdev/internal/auth"
	"github.com/orchard9/rdev/internal/domain"
	"github.com/orchard9/rdev/internal/sdlc"
	"github.com/orchard9/rdev/internal/service"
	"github.com/orchard9/rdev/pkg/api"
)

// SDLCHandler handles SDLC endpoints for project lifecycle management.
type SDLCHandler struct {
	sdlcService *service.SDLCService
}

// NewSDLCHandler creates a new SDLC handler.
func NewSDLCHandler(sdlcService *service.SDLCService) *SDLCHandler {
	return &SDLCHandler{
		sdlcService: sdlcService,
	}
}

// Mount registers all SDLC routes under /projects/{id}/sdlc/.
func (h *SDLCHandler) Mount(r api.Router) {
	r.Route("/projects/{id}/sdlc", func(r chi.Router) {
		// State (read)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/state", h.GetState)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/next", h.GetNext)

		// Features - read
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/features", h.ListFeatures)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/features/{slug}", h.GetFeature)

		// Features - write
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features", h.CreateFeature)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/transition", h.TransitionFeature)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/block", h.BlockFeature)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/unblock", h.UnblockFeature)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Delete("/features/{slug}", h.DeleteFeature)

		// Artifacts - read
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/features/{slug}/artifacts", h.GetArtifactStatus)

		// Artifacts - write
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/artifacts/{type}/approve", h.ApproveArtifact)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/artifacts/{type}/reject", h.RejectArtifact)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/artifacts/{type}/pass", h.PassArtifact)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/artifacts/{type}/fail", h.FailArtifact)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/artifacts/{type}/needs-fix", h.NeedsFixArtifact)

		// Tasks - read
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/features/{slug}/tasks", h.ListTasks)

		// Tasks - write
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/tasks", h.AddTask)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/tasks/{taskId}/start", h.StartTask)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/tasks/{taskId}/complete", h.CompleteTask)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/tasks/{taskId}/block", h.BlockTask)

		// Branches - read
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/features/{slug}/branches", h.GetBranchStatus)

		// Branches - write
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/branches", h.CreateBranch)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/branches/sync", h.SyncBranch)

		// Merge / Archive (write)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/merge", h.MergeFeature)
		r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).Post("/features/{slug}/archive", h.ArchiveFeature)

		// Queries (read)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/query/blocked", h.QueryBlocked)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/query/ready", h.QueryReady)
		r.With(auth.RequireScope(auth.ScopeProjectsRead, auth.ScopeAdmin)).Get("/query/needs-approval", h.QueryNeedsApproval)
	})
}

// GetState returns the global SDLC state for a project.
// GET /projects/{id}/sdlc/state
func (h *SDLCHandler) GetState(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")

	ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
	defer cancel()

	state, err := h.sdlcService.GetState(ctx, projectID)
	if err != nil {
		writeSDLCError(w, r, err)
		return
	}

	api.WriteSuccess(w, r, state)
}

// GetNext returns the classifier's recommendation for the next action.
// GET /projects/{id}/sdlc/next?feature=slug
func (h *SDLCHandler) GetNext(w http.ResponseWriter, r *http.Request) {
	projectID := chi.URLParam(r, "id")
	feature := r.URL.Query().Get("feature")

	ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
	defer cancel()

	cl, err := h.sdlcService.GetNext(ctx, projectID, feature)
	if err != nil {
		writeSDLCError(w, r, err)
		return
	}

	api.WriteSuccess(w, r, cl)
}

// writeSDLCError maps SDLC domain errors to HTTP responses.
func writeSDLCError(w http.ResponseWriter, r *http.Request, err error) {
	switch {
	case errors.Is(err, domain.ErrProjectNotFound):
		api.WriteNotFound(w, r, "project not found")
	case errors.Is(err, sdlc.ErrNotInitialized):
		api.WriteNotFound(w, r, "sdlc not initialized for this project")
	case errors.Is(err, sdlc.ErrFeatureNotFound):
		api.WriteNotFound(w, r, "feature not found")
	case errors.Is(err, sdlc.ErrTaskNotFound):
		api.WriteNotFound(w, r, "task not found")
	case errors.Is(err, sdlc.ErrArtifactNotFound):
		api.WriteNotFound(w, r, "artifact not found")
	case errors.Is(err, sdlc.ErrFeatureExists):
		api.WriteBadRequest(w, r, "feature already exists")
	case errors.Is(err, sdlc.ErrInvalidTransition):
		api.WriteBadRequest(w, r, err.Error())
	case errors.Is(err, sdlc.ErrInvalidPhase):
		api.WriteBadRequest(w, r, "invalid phase")
	case errors.Is(err, sdlc.ErrInvalidSlug):
		api.WriteBadRequest(w, r, "invalid slug: must be lowercase alphanumeric with hyphens")
	case errors.Is(err, sdlc.ErrInvalidArtifact):
		api.WriteBadRequest(w, r, "invalid artifact type")
	case errors.Is(err, sdlc.ErrBranchExists):
		api.WriteBadRequest(w, r, "branch already exists")
	case errors.Is(err, sdlc.ErrBranchNotFound):
		api.WriteNotFound(w, r, "branch not found")
	case errors.Is(err, sdlc.ErrMergeNotReady):
		api.WriteBadRequest(w, r, "feature not ready to merge: unmet gates")
	default:
		api.WriteInternalError(w, r, "sdlc operation failed")
	}
}
