// Package restheadspec provides the Rest Header Spec API framework.
//
// Rest Header Spec (restheadspec) is a RESTful API framework that reads query options,
// filters, sorting, pagination, and other parameters from HTTP headers instead of
// request bodies or query parameters. This approach provides a clean separation between
// data and metadata in API requests.
//
// # Key Features
//
//   - Header-based API configuration: All query options are passed via HTTP headers
//   - Database-agnostic: Works with both GORM and Bun ORM through adapters
//   - Router-agnostic: Supports multiple HTTP routers (Mux, BunRouter, etc.)
//   - Advanced filtering: Supports complex filter operations (eq, gt, lt, like, between, etc.)
//   - Pagination and sorting: Built-in support for limit, offset, and multi-column sorting
//   - Preloading and expansion: Support for eager loading relationships
//   - Multiple response formats: Default, simple, and Syncfusion formats
//
// # HTTP Headers
//
// The following headers are supported for configuring API requests:
//
//   - X-Filters: JSON array of filter conditions
//   - X-Columns: Comma-separated list of columns to select
//   - X-Sort: JSON array of sort specifications
//   - X-Limit: Maximum number of records to return
//   - X-Offset: Number of records to skip
//   - X-Preload: Comma-separated list of relations to preload
//   - X-Expand: Comma-separated list of relations to expand (LEFT JOIN)
//   - X-Distinct: Boolean to enable DISTINCT queries
//   - X-Skip-Count: Boolean to skip total count query
//   - X-Response-Format: Response format (detail, simple, syncfusion)
//   - X-Clean-JSON: Boolean to remove null/empty fields
//   - X-Custom-SQL-Where: Custom SQL WHERE clause (AND)
//   - X-Custom-SQL-Or: Custom SQL WHERE clause (OR)
//   - X-Custom-SQL-Join: Custom SQL JOIN clauses (pipe-separated for multiple)
//
// # Usage Example
//
//	// Create a handler with GORM
//	handler := restheadspec.NewHandlerWithGORM(db)
//
//	// Register models
//	handler.Registry.RegisterModel("users", User{})
//
//	// Setup routes with Mux
//	muxRouter := mux.NewRouter()
//	restheadspec.SetupMuxRoutes(muxRouter, handler)
//
//	// Make a request with headers
//	// GET /public/users
//	// X-Filters: [{"column":"age","operator":"gt","value":18}]
//	// X-Sort: [{"column":"name","direction":"asc"}]
//	// X-Limit: 10
package restheadspec

import (
	"net/http"
	"runtime/debug"
	"strings"

	"github.com/gorilla/mux"
	"github.com/uptrace/bun"
	"github.com/uptrace/bunrouter"
	"gorm.io/gorm"

	"github.com/bitechdev/ResolveSpec/pkg/common"
	"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
	"github.com/bitechdev/ResolveSpec/pkg/common/adapters/router"
	"github.com/bitechdev/ResolveSpec/pkg/logger"
	"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
)

// NewHandlerWithGORM creates a new Handler with GORM adapter
func NewHandlerWithGORM(db *gorm.DB) *Handler {
	gormAdapter := database.NewGormAdapter(db)
	registry := modelregistry.NewModelRegistry()
	return NewHandler(gormAdapter, registry)
}

// NewHandlerWithBun creates a new Handler with Bun adapter
func NewHandlerWithBun(db *bun.DB) *Handler {
	bunAdapter := database.NewBunAdapter(db)
	registry := modelregistry.NewModelRegistry()
	return NewHandler(bunAdapter, registry)
}

// NewStandardMuxRouter creates a router with standard Mux HTTP handlers
func NewStandardMuxRouter() *router.StandardMuxAdapter {
	return router.NewStandardMuxAdapter()
}

// NewStandardBunRouter creates a router with standard BunRouter handlers
func NewStandardBunRouter() *router.StandardBunRouterAdapter {
	return router.NewStandardBunRouterAdapter()
}

// MiddlewareFunc is a function that wraps an http.Handler with additional functionality
type MiddlewareFunc func(http.Handler) http.Handler

// SetupMuxRoutes sets up routes for the RestHeadSpec API with Mux
// authMiddleware is optional - if provided, routes will be protected with the middleware
// Example: SetupMuxRoutes(router, handler, func(h http.Handler) http.Handler { return security.NewAuthHandler(securityList, h) })
func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware MiddlewareFunc) {
	// Add global /openapi route
	openAPIHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		corsConfig := common.DefaultCORSConfig()
		respAdapter := router.NewHTTPResponseWriter(w)
		reqAdapter := router.NewHTTPRequest(r)
		common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)

		handler.HandleOpenAPI(respAdapter, reqAdapter)
	})
	muxRouter.Handle("/openapi", openAPIHandler).Methods("GET", "OPTIONS")

	// Get all registered models from the registry
	allModels := handler.registry.GetAllModels()

	// Loop through each registered model and create explicit routes
	for fullName := range allModels {
		// Parse the full name (e.g., "public.users" or just "users")
		schema, entity := parseModelName(fullName)

		// Build the route paths
		entityPath := buildRoutePath(schema, entity)
		entityWithIDPath := buildRoutePath(schema, entity) + "/{id}"
		metadataPath := buildRoutePath(schema, entity) + "/metadata"

		// Create handler functions for this specific entity
		var entityHandler http.Handler = createMuxHandler(handler, schema, entity, "")
		var entityWithIDHandler http.Handler = createMuxHandler(handler, schema, entity, "id")
		var metadataHandler http.Handler = createMuxGetHandler(handler, schema, entity, "")
		optionsEntityHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "POST", "OPTIONS"})
		optionsEntityWithIDHandler := createMuxOptionsHandler(handler, schema, entity, []string{"GET", "PUT", "PATCH", "DELETE", "POST", "OPTIONS"})

		// Apply authentication middleware if provided
		if authMiddleware != nil {
			entityHandler = authMiddleware(entityHandler)
			entityWithIDHandler = authMiddleware(entityWithIDHandler)
			metadataHandler = authMiddleware(metadataHandler)
			// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
		}

		// Register routes for this entity
		// IMPORTANT: Register more specific routes before wildcard routes

		// GET, POST for /{schema}/{entity}
		muxRouter.Handle(entityPath, entityHandler).Methods("GET", "POST")

		// GET for metadata (using HandleGet) - MUST be registered before /{id} route
		muxRouter.Handle(metadataPath, metadataHandler).Methods("GET")

		// GET, PUT, PATCH, DELETE, POST for /{schema}/{entity}/{id}
		muxRouter.Handle(entityWithIDPath, entityWithIDHandler).Methods("GET", "PUT", "PATCH", "DELETE", "POST")

		// OPTIONS for CORS preflight - returns metadata
		muxRouter.Handle(entityPath, optionsEntityHandler).Methods("OPTIONS")
		muxRouter.Handle(entityWithIDPath, optionsEntityWithIDHandler).Methods("OPTIONS")
	}
}

// Helper function to create Mux handler for a specific entity with CORS support
func createMuxHandler(handler *Handler, schema, entity, idParam string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Set CORS headers
		corsConfig := common.DefaultCORSConfig()
		respAdapter := router.NewHTTPResponseWriter(w)
		reqAdapter := router.NewHTTPRequest(r)
		common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)

		vars := make(map[string]string)
		vars["schema"] = schema
		vars["entity"] = entity
		if idParam != "" {
			vars["id"] = mux.Vars(r)[idParam]
		}

		handler.Handle(respAdapter, reqAdapter, vars)
	}
}

// Helper function to create Mux GET handler for a specific entity with CORS support
func createMuxGetHandler(handler *Handler, schema, entity, idParam string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Set CORS headers
		corsConfig := common.DefaultCORSConfig()
		respAdapter := router.NewHTTPResponseWriter(w)
		reqAdapter := router.NewHTTPRequest(r)
		common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)

		vars := make(map[string]string)
		vars["schema"] = schema
		vars["entity"] = entity
		if idParam != "" {
			vars["id"] = mux.Vars(r)[idParam]
		}

		handler.HandleGet(respAdapter, reqAdapter, vars)
	}
}

// Helper function to create Mux OPTIONS handler that returns metadata
func createMuxOptionsHandler(handler *Handler, schema, entity string, allowedMethods []string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		// Set CORS headers with the allowed methods for this route
		corsConfig := common.DefaultCORSConfig()
		corsConfig.AllowedMethods = allowedMethods
		respAdapter := router.NewHTTPResponseWriter(w)
		reqAdapter := router.NewHTTPRequest(r)
		common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)

		// Return metadata in the OPTIONS response body
		vars := make(map[string]string)
		vars["schema"] = schema
		vars["entity"] = entity

		handler.HandleGet(respAdapter, reqAdapter, vars)
	}
}

// parseModelName parses a model name like "public.users" into schema and entity
// If no schema is present, returns empty string for schema
func parseModelName(fullName string) (schema, entity string) {
	parts := strings.Split(fullName, ".")
	if len(parts) == 2 {
		return parts[0], parts[1]
	}
	return "", fullName
}

// buildRoutePath builds a route path from schema and entity
// If schema is empty, returns just "/entity", otherwise "/{schema}/{entity}"
func buildRoutePath(schema, entity string) string {
	if schema == "" {
		return "/" + entity
	}
	return "/" + schema + "/" + entity
}

// Example usage functions for documentation:

// ExampleWithGORM shows how to use RestHeadSpec with GORM
func ExampleWithGORM(db *gorm.DB) {
	// Create handler using GORM
	handler := NewHandlerWithGORM(db)

	// Setup router without authentication
	muxRouter := mux.NewRouter()
	SetupMuxRoutes(muxRouter, handler, nil)

	// Register models
	// handler.registry.RegisterModel("public.users", &User{})

	// To add authentication, pass a middleware function:
	// import "github.com/bitechdev/ResolveSpec/pkg/security"
	// secList := security.NewSecurityList(myProvider)
	// authMiddleware := func(h http.Handler) http.Handler {
	//     return security.NewAuthHandler(secList, h)
	// }
	// SetupMuxRoutes(muxRouter, handler, authMiddleware)
}

// ExampleWithBun shows how to switch to Bun ORM
func ExampleWithBun(bunDB *bun.DB) {
	// Create Bun adapter
	dbAdapter := database.NewBunAdapter(bunDB)

	// Create model registry
	registry := modelregistry.NewModelRegistry()
	// registry.RegisterModel("public.users", &User{})

	// Create handler
	handler := NewHandler(dbAdapter, registry)

	// Setup routes without authentication
	muxRouter := mux.NewRouter()
	SetupMuxRoutes(muxRouter, handler, nil)
}

// BunRouterHandler is an interface that both bunrouter.Router and bunrouter.Group implement
type BunRouterHandler interface {
	Handle(method, path string, handler bunrouter.HandlerFunc)
}

// wrapBunRouterHandler wraps a bunrouter handler with auth middleware if provided
func wrapBunRouterHandler(handler bunrouter.HandlerFunc, authMiddleware MiddlewareFunc) bunrouter.HandlerFunc {
	if authMiddleware == nil {
		return handler
	}

	return func(w http.ResponseWriter, req bunrouter.Request) error {
		// Create an http.Handler that calls the bunrouter handler
		httpHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			// Replace the embedded *http.Request with the middleware-enriched one
			// so that auth context (user ID, etc.) is visible to the handler.
			enrichedReq := req
			enrichedReq.Request = r
			_ = handler(w, enrichedReq)
		})

		// Wrap with auth middleware and execute
		wrappedHandler := authMiddleware(httpHandler)
		wrappedHandler.ServeHTTP(w, req.Request)

		return nil
	}
}

// SetupBunRouterRoutes sets up bunrouter routes for the RestHeadSpec API
// Accepts bunrouter.Router or bunrouter.Group
// authMiddleware is optional - if provided, routes will be protected with the middleware
func SetupBunRouterRoutes(r BunRouterHandler, handler *Handler, authMiddleware MiddlewareFunc) {
	defer func() {
		if rec := recover(); rec != nil {
			logger.Error("panic in restheadspec.SetupBunRouterRoutes: %v\n%s", rec, debug.Stack())
		}
	}()

	// CORS config
	corsConfig := common.DefaultCORSConfig()

	// Add global /openapi route
	r.Handle("GET", "/openapi", func(w http.ResponseWriter, req bunrouter.Request) error {
		respAdapter := router.NewHTTPResponseWriter(w)
		reqAdapter := router.NewBunRouterRequest(req)
		common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
		handler.HandleOpenAPI(respAdapter, reqAdapter)
		return nil
	})

	r.Handle("OPTIONS", "/openapi", func(w http.ResponseWriter, req bunrouter.Request) error {
		respAdapter := router.NewHTTPResponseWriter(w)
		reqAdapter := router.NewHTTPRequest(req.Request)
		common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
		return nil
	})

	// Get all registered models from the registry
	allModels := handler.registry.GetAllModels()

	// Loop through each registered model and create explicit routes
	for fullName := range allModels {
		func() {
			defer func() {
				if rec := recover(); rec != nil {
					logger.Error("panic registering restheadspec routes for model %s: %v\n%s", fullName, rec, debug.Stack())
				}
			}()

			// Parse the full name (e.g., "public.users" or just "users")
			schema, entity := parseModelName(fullName)

			// Build the route paths
			entityPath := buildRoutePath(schema, entity)
			entityWithIDPath := entityPath + "/:id"
			metadataPath := entityPath + "/metadata"

			// Create closure variables to capture current schema and entity
			currentSchema := schema
			currentEntity := entity

			// GET and POST for /{schema}/{entity}
			getEntityHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("GET", entityPath, wrapBunRouterHandler(getEntityHandler, authMiddleware))

			postEntityHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("POST", entityPath, wrapBunRouterHandler(postEntityHandler, authMiddleware))

			// GET, POST, PUT, PATCH, DELETE for /{schema}/{entity}/:id
			getEntityWithIDHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
					"id":     req.Param("id"),
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("GET", entityWithIDPath, wrapBunRouterHandler(getEntityWithIDHandler, authMiddleware))

			postEntityWithIDHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
					"id":     req.Param("id"),
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("POST", entityWithIDPath, wrapBunRouterHandler(postEntityWithIDHandler, authMiddleware))

			putEntityWithIDHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
					"id":     req.Param("id"),
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("PUT", entityWithIDPath, wrapBunRouterHandler(putEntityWithIDHandler, authMiddleware))

			patchEntityWithIDHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
					"id":     req.Param("id"),
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("PATCH", entityWithIDPath, wrapBunRouterHandler(patchEntityWithIDHandler, authMiddleware))

			deleteEntityWithIDHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
					"id":     req.Param("id"),
				}

				handler.Handle(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("DELETE", entityWithIDPath, wrapBunRouterHandler(deleteEntityWithIDHandler, authMiddleware))

			// Metadata endpoint
			metadataHandler := func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				common.SetCORSHeaders(respAdapter, reqAdapter, corsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
				}

				handler.HandleGet(respAdapter, reqAdapter, params)
				return nil
			}
			r.Handle("GET", metadataPath, wrapBunRouterHandler(metadataHandler, authMiddleware))

			// OPTIONS route without ID (returns metadata)
			// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
			r.Handle("OPTIONS", entityPath, func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				optionsCorsConfig := corsConfig
				optionsCorsConfig.AllowedMethods = []string{"GET", "POST", "OPTIONS"}
				common.SetCORSHeaders(respAdapter, reqAdapter, optionsCorsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
				}

				handler.HandleGet(respAdapter, reqAdapter, params)
				return nil
			})

			// OPTIONS route with ID (returns metadata)
			// Don't apply auth middleware to OPTIONS - CORS preflight must not require auth
			r.Handle("OPTIONS", entityWithIDPath, func(w http.ResponseWriter, req bunrouter.Request) error {
				respAdapter := router.NewHTTPResponseWriter(w)
				reqAdapter := router.NewBunRouterRequest(req)
				optionsCorsConfig := corsConfig
				optionsCorsConfig.AllowedMethods = []string{"GET", "PUT", "PATCH", "DELETE", "POST", "OPTIONS"}
				common.SetCORSHeaders(respAdapter, reqAdapter, optionsCorsConfig)
				params := map[string]string{
					"schema": currentSchema,
					"entity": currentEntity,
				}

				handler.HandleGet(respAdapter, reqAdapter, params)
				return nil
			})

			logger.Info("Registered restheadspec bunrouter routes for model %s at %s", fullName, entityPath)
		}()
	}
}

// ExampleBunRouterWithBunDB shows usage with both BunRouter and Bun DB
func ExampleBunRouterWithBunDB(bunDB *bun.DB) {
	// Create handler
	handler := NewHandlerWithBun(bunDB)

	// Create bunrouter
	bunRouter := bunrouter.New()

	// Setup routes without authentication
	SetupBunRouterRoutes(bunRouter, handler, nil)

	// Start server
	if err := http.ListenAndServe(":8080", bunRouter); err != nil {
		logger.Error("Server failed to start: %v", err)
	}
}

// ExampleBunRouterWithGroup shows how to use SetupBunRouterRoutes with a bunrouter.Group
func ExampleBunRouterWithGroup(bunDB *bun.DB) {
	// Create handler with Bun adapter
	handler := NewHandlerWithBun(bunDB)

	// Create bunrouter
	bunRouter := bunrouter.New()

	// Create a route group with a prefix
	apiGroup := bunRouter.NewGroup("/api")

	// Setup RestHeadSpec routes on the group - routes will be under /api
	SetupBunRouterRoutes(apiGroup, handler, nil)

	// Start server
	if err := http.ListenAndServe(":8080", bunRouter); err != nil {
		logger.Error("Server failed to start: %v", err)
	}
}
