package handlers

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"

	"chat-switchboard/config"
	authpkg "chat-switchboard/auth"
	"chat-switchboard/database"
	"chat-switchboard/middleware"
	"chat-switchboard/pages"
	"chat-switchboard/store"
	postgres "chat-switchboard/store/postgres"
	sqlite "chat-switchboard/store/sqlite"
)

// TestRouteRegistration verifies that ALL production routes can be
// registered on a single Gin engine without panicking. This catches
// wildcard parameter name conflicts (e.g. /w/:id vs /w/:scope/:slug)
// which cause runtime panics during startup.
//
// The test mirrors the route structure in main.go and pages.go.
// Any new route group should be added here.
func TestRouteRegistration(t *testing.T) {
	database.RequireTestDB(t)

	cfg := &config.Config{
		JWTSecret: testJWTSecret,
		BasePath:  "/test",
		Port:      "0",
	}

	var stores store.Stores
	if database.IsSQLite() {
		stores = sqlite.NewStores(database.TestDB)
	} else {
		stores = postgres.NewStores(database.TestDB)
	}
	userCache := middleware.NewUserStatusCache()

	// This must not panic. If it does, there's a wildcard conflict.
	r := gin.New()
	base := r.Group(cfg.BasePath)

	// ── API routes (mirrors main.go) ────────

	api := base.Group("/api/v1")

	// Auth (public)
	auth := NewAuthHandler(cfg, stores, nil, authpkg.NewBuiltinProvider())
	api.POST("/auth/login", auth.Login)
	api.POST("/auth/register", auth.Register)

	// Protected API routes
	protected := api.Group("")
	protected.Use(middleware.Auth(cfg, stores.Users, userCache))

	// Channels (the route group that conflicts with workflow)
	channels := NewChannelHandler(stores)
	protected.GET("/channels", channels.ListChannels)
	protected.POST("/channels", channels.CreateChannel)
	protected.GET("/channels/:id", channels.GetChannel)
	protected.DELETE("/channels/:id", channels.DeleteChannel)

	// Messages
	msgs := NewMessageHandler(nil, stores, nil, nil)
	protected.GET("/channels/:id/messages", msgs.ListMessages)
	protected.POST("/channels/:id/messages", msgs.CreateMessage)

	// Workflow CRUD
	wfH := NewWorkflowHandler(stores)
	protected.GET("/workflows", wfH.List)
	protected.POST("/workflows", wfH.Create)
	protected.GET("/workflows/:id", wfH.Get)
	protected.PATCH("/workflows/:id", wfH.Update)
	protected.DELETE("/workflows/:id", wfH.Delete)
	protected.GET("/workflows/:id/stages", wfH.ListStages)
	protected.POST("/workflows/:id/stages", wfH.CreateStage)
	protected.PUT("/workflows/:id/stages/:sid", wfH.UpdateStage)
	protected.DELETE("/workflows/:id/stages/:sid", wfH.DeleteStage)
	protected.PATCH("/workflows/:id/stages/reorder", wfH.ReorderStages)
	protected.POST("/workflows/:id/publish", wfH.Publish)
	protected.GET("/workflows/:id/versions/:version", wfH.GetVersion)

	// Workflow instances
	wfInstH := NewWorkflowInstanceHandler(stores, nil, nil, nil)
	protected.POST("/workflows/:id/start", wfInstH.Start)
	protected.GET("/channels/:id/workflow/status", wfInstH.GetStatus)
	protected.POST("/channels/:id/workflow/advance", wfInstH.Advance)
	protected.POST("/channels/:id/workflow/reject", wfInstH.Reject)

	// Workflow assignments (v0.26.4)
	wfAssignH := NewWorkflowAssignmentHandler(stores)
	protected.GET("/workflow-assignments/mine", wfAssignH.ListMine)
	protected.POST("/workflow-assignments/:id/claim", wfAssignH.Claim)
	protected.POST("/workflow-assignments/:id/complete", wfAssignH.Complete)

	// Session API (the /w/:id group)
	wfAPI := base.Group("/api/v1/w")
	wfAPI.Use(middleware.AuthOrSession(cfg, stores, userCache))
	wfAPI.POST("/:id/messages", msgs.CreateMessage)
	wfAPI.GET("/:id/messages", msgs.ListMessages)

	// Visitor entry (separate namespace to avoid /w/:id collision)
	wfEntry := NewWorkflowEntryHandler(stores)
	base.POST("/api/v1/workflow-entry/:scope/:slug", wfEntry.StartVisitor)

	// ── Page routes (mirrors pages.go surface registration) ────

	pageEngine := pages.New(cfg, stores)
	pageEngine.RegisterPageRoutes(base, pages.PageRouteMiddleware{
		Authenticated: middleware.AuthOrRedirect(cfg, stores.Users, userCache),
		Admin:         []gin.HandlerFunc{middleware.AuthOrRedirect(cfg, stores.Users, userCache), middleware.RequireAdminPage()},
		Session:       middleware.AuthOrSession(cfg, stores, userCache),
	})

	// ── Verify routes actually resolve ────

	// Health-check style: send a request and confirm we get a response
	// (not a panic). We don't care about the status code — just that
	// the router doesn't blow up.
	paths := []struct {
		method string
		path   string
	}{
		{"GET", "/test/api/v1/workflows"},
		{"GET", "/test/api/v1/channels"},
		{"GET", "/test/w/some-channel-id"},         // workflow chat surface
		{"GET", "/test/w/some-scope/some-slug"},     // workflow landing surface
		{"POST", "/test/api/v1/workflow-entry/global/test-slug"},
	}

	for _, p := range paths {
		req := httptest.NewRequest(p.method, p.path, nil)
		w := httptest.NewRecorder()
		r.ServeHTTP(w, req)

		// We just want to confirm the router didn't panic and returned
		// something (even 401/404 is fine — means routing worked).
		if w.Code == 0 {
			t.Errorf("%s %s: got status 0 (router failed to match)", p.method, p.path)
		}
	}
}

// TestWorkflowPageRoutesNoConflict specifically tests the /w/ namespace
// to ensure different-depth routes with the same param name work.
func TestWorkflowPageRoutesNoConflict(t *testing.T) {
	r := gin.New()

	// These two routes MUST use the same wildcard name at position 1.
	// /w/:id (2 segments) = workflow chat
	// /w/:id/:slug (3 segments) = workflow landing
	// Using different names (e.g. :scope) at the same position panics.
	r.GET("/w/:id", func(c *gin.Context) {
		c.String(http.StatusOK, "chat:"+c.Param("id"))
	})
	r.GET("/w/:id/:slug", func(c *gin.Context) {
		c.String(http.StatusOK, "landing:"+c.Param("id")+"/"+c.Param("slug"))
	})

	// 2-segment path → chat
	w := httptest.NewRecorder()
	r.ServeHTTP(w, httptest.NewRequest("GET", "/w/channel-123", nil))
	if w.Code != 200 || w.Body.String() != "chat:channel-123" {
		t.Errorf("2-segment: got %d %q", w.Code, w.Body.String())
	}

	// 3-segment path → landing
	w = httptest.NewRecorder()
	r.ServeHTTP(w, httptest.NewRequest("GET", "/w/my-team/intake-form", nil))
	if w.Code != 200 || w.Body.String() != "landing:my-team/intake-form" {
		t.Errorf("3-segment: got %d %q", w.Code, w.Body.String())
	}
}
