package api

import (
	"bufio"
	"context"
	"encoding/json"
	"fmt"
	"io/fs"
	"math"
	"net/http"
	"os"
	"runtime"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"

	"git.warky.dev/wdevs/whatshooked/pkg/config"
	"git.warky.dev/wdevs/whatshooked/pkg/handlers"
	"git.warky.dev/wdevs/whatshooked/pkg/models"
	"git.warky.dev/wdevs/whatshooked/pkg/serverembed"
	"git.warky.dev/wdevs/whatshooked/pkg/storage"
	"github.com/bitechdev/ResolveSpec/pkg/common"
	"github.com/bitechdev/ResolveSpec/pkg/common/adapters/database"
	"github.com/bitechdev/ResolveSpec/pkg/modelregistry"
	"github.com/bitechdev/ResolveSpec/pkg/restheadspec"
	"github.com/bitechdev/ResolveSpec/pkg/security"
	"github.com/bitechdev/ResolveSpec/pkg/server"
	"github.com/gorilla/mux"
	"github.com/uptrace/bun"
	"golang.org/x/crypto/bcrypt"
)

// WhatsHookedInterface defines the interface for accessing WhatsHooked components
type WhatsHookedInterface interface {
	Handlers() *handlers.Handlers
}

// Server represents the API server
type Server struct {
	serverMgr   server.Manager
	handler     *restheadspec.Handler
	secProvider security.SecurityProvider
	db          *bun.DB
	config      *config.Config
	wh          WhatsHookedInterface
}

type systemStatsSampler struct {
	mu               sync.Mutex
	lastCPUJiffies   uint64
	lastCPUTimestamp time.Time
	lastNetTotal     uint64
	lastNetTimestamp time.Time
}

var runtimeStatsSampler = &systemStatsSampler{}

// NewServer creates a new API server with ResolveSpec integration
func NewServer(cfg *config.Config, db *bun.DB, wh WhatsHookedInterface) (*Server, error) {
	// Create model registry and register models
	registry := modelregistry.NewModelRegistry()
	registerModelsToRegistry(registry)

	// Create BUN adapter
	bunAdapter := database.NewBunAdapter(db)

	// Create ResolveSpec handler with registry
	handler := restheadspec.NewHandler(bunAdapter, registry)

	// Create security provider
	secProvider := NewSecurityProvider(cfg.Server.JWTSecret, db, cfg)

	// Create security list and register hooks
	securityList, err := security.NewSecurityList(secProvider)
	if err != nil {
		return nil, fmt.Errorf("failed to create security list: %w", err)
	}
	restheadspec.RegisterSecurityHooks(handler, securityList)

	// Ensure legacy /api/* handlers use the same ResolveSpec auth method.
	// This keeps JWT + phase1 auth behavior aligned across /api and /api/v1.
	wh.Handlers().WithAuthConfig(&handlers.AuthConfig{
		Validator: func(r *http.Request) bool {
			_, err := secProvider.Authenticate(r)
			return err == nil
		},
	})

	// Create router
	router := mux.NewRouter()

	// Add CORS middleware to all routes
	router.Use(corsMiddleware)

	// Create a subrouter for /api/v1/* routes that need JWT authentication
	apiV1Router := router.PathPrefix("/api/v1").Subrouter()
	apiV1Router.Use(security.NewAuthMiddleware(securityList))
	apiV1Router.Use(security.SetSecurityMiddleware(securityList))

	// Create the embedded dist FS (built Vite output, includes web/public/ contents)
	distFS, err := fs.Sub(serverembed.RootEmbedFS, "dist")
	if err != nil {
		return nil, fmt.Errorf("failed to sub embedded dist FS: %w", err)
	}

	// Setup WhatsApp API routes on main router (these use their own Auth middleware)
	SetupWhatsAppRoutes(router, wh, distFS)

	// Setup ResolveSpec routes on the protected /api/v1 subrouter (auto-generated CRUD)
	restheadspec.SetupMuxRoutes(apiV1Router, handler, nil)
	apiV1Router.HandleFunc("/system/stats", handleSystemStats).Methods("GET")
	apiV1Router.HandleFunc("/cache/stats", wh.Handlers().GetCacheStats).Methods("GET")

	// Add custom routes (login, logout, etc.) on main router
	SetupCustomRoutes(router, secProvider, db)

	// Serve React SPA from the embedded filesystem at /ui/
	spa := embeddedSPAHandler{fs: distFS, indexPath: "index.html"}
	router.PathPrefix("/ui/").Handler(http.StripPrefix("/ui", spa))
	router.PathPrefix("/ui").Handler(http.StripPrefix("/ui", spa))

	// Create server manager
	serverMgr := server.NewManager()

	// Add HTTP server
	_, err = serverMgr.Add(server.Config{
		Name:            "whatshooked",
		Host:            cfg.Server.Host,
		Port:            cfg.Server.Port,
		Handler:         router,
		GZIP:            true,
		ShutdownTimeout: 30 * time.Second,
		DrainTimeout:    25 * time.Second,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to add server: %w", err)
	}

	// Register shutdown callback for database
	serverMgr.RegisterShutdownCallback(func(ctx context.Context) error {
		return storage.Close()
	})

	return &Server{
		serverMgr:   serverMgr,
		handler:     handler,
		secProvider: secProvider,
		db:          db,
		config:      cfg,
		wh:          wh,
	}, nil
}

func handleSystemStats(w http.ResponseWriter, _ *http.Request) {
	var mem runtime.MemStats
	runtime.ReadMemStats(&mem)

	cpuPercent := runtimeStatsSampler.sampleCPUPercent()
	rxBytes, txBytes := readNetworkBytes()
	netTotal := rxBytes + txBytes
	netBytesPerSec := runtimeStatsSampler.sampleNetworkBytesPerSec(netTotal)

	writeJSON(w, http.StatusOK, map[string]any{
		"go_memory_bytes":       mem.Alloc,
		"go_memory_mb":          float64(mem.Alloc) / (1024.0 * 1024.0),
		"go_sys_memory_bytes":   mem.Sys,
		"go_sys_memory_mb":      float64(mem.Sys) / (1024.0 * 1024.0),
		"go_goroutines":         runtime.NumGoroutine(),
		"go_cpu_percent":        cpuPercent,
		"network_rx_bytes":      rxBytes,
		"network_tx_bytes":      txBytes,
		"network_total_bytes":   netTotal,
		"network_bytes_per_sec": netBytesPerSec,
	})
}

func readProcessCPUJiffies() (uint64, bool) {
	data, err := os.ReadFile("/proc/self/stat")
	if err != nil {
		return 0, false
	}

	parts := strings.Fields(string(data))
	if len(parts) < 15 {
		return 0, false
	}

	utime, err := strconv.ParseUint(parts[13], 10, 64)
	if err != nil {
		return 0, false
	}
	stime, err := strconv.ParseUint(parts[14], 10, 64)
	if err != nil {
		return 0, false
	}

	return utime + stime, true
}

func (s *systemStatsSampler) sampleCPUPercent() float64 {
	const clockTicksPerSecond = 100.0 // Linux default USER_HZ

	jiffies, ok := readProcessCPUJiffies()
	if !ok {
		return 0
	}

	now := time.Now()

	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lastCPUTimestamp.IsZero() || s.lastCPUJiffies == 0 {
		s.lastCPUTimestamp = now
		s.lastCPUJiffies = jiffies
		return 0
	}

	elapsed := now.Sub(s.lastCPUTimestamp).Seconds()
	deltaJiffies := jiffies - s.lastCPUJiffies

	s.lastCPUTimestamp = now
	s.lastCPUJiffies = jiffies

	if elapsed <= 0 {
		return 0
	}

	cpuSeconds := float64(deltaJiffies) / clockTicksPerSecond
	cores := float64(runtime.NumCPU())
	if cores < 1 {
		cores = 1
	}

	percent := (cpuSeconds / elapsed) * 100.0 / cores
	if percent < 0 {
		return 0
	}
	if percent > 100 {
		return 100
	}

	return math.Round(percent*100) / 100
}

func readNetworkBytes() (uint64, uint64) {
	file, err := os.Open("/proc/net/dev")
	if err != nil {
		return 0, 0
	}
	defer file.Close()

	var rxBytes uint64
	var txBytes uint64

	scanner := bufio.NewScanner(file)
	lineNo := 0
	for scanner.Scan() {
		lineNo++
		// Skip headers.
		if lineNo <= 2 {
			continue
		}

		line := strings.TrimSpace(scanner.Text())
		if line == "" {
			continue
		}

		parts := strings.SplitN(line, ":", 2)
		if len(parts) != 2 {
			continue
		}
		iface := strings.TrimSpace(parts[0])
		if iface == "lo" {
			continue
		}

		fields := strings.Fields(parts[1])
		if len(fields) < 16 {
			continue
		}

		rx, err := strconv.ParseUint(fields[0], 10, 64)
		if err == nil {
			rxBytes += rx
		}
		tx, err := strconv.ParseUint(fields[8], 10, 64)
		if err == nil {
			txBytes += tx
		}
	}

	return rxBytes, txBytes
}

func (s *systemStatsSampler) sampleNetworkBytesPerSec(totalBytes uint64) float64 {
	now := time.Now()

	s.mu.Lock()
	defer s.mu.Unlock()

	if s.lastNetTimestamp.IsZero() {
		s.lastNetTimestamp = now
		s.lastNetTotal = totalBytes
		return 0
	}

	elapsed := now.Sub(s.lastNetTimestamp).Seconds()
	deltaBytes := totalBytes - s.lastNetTotal

	s.lastNetTimestamp = now
	s.lastNetTotal = totalBytes

	if elapsed <= 0 {
		return 0
	}

	bps := float64(deltaBytes) / elapsed
	if bps < 0 {
		return 0
	}

	return math.Round(bps*100) / 100
}

// Start starts the API server
func (s *Server) Start() error {
	return s.serverMgr.ServeWithGracefulShutdown()
}

// registerModelsToRegistry registers all BUN models with the model registry
func registerModelsToRegistry(registry common.ModelRegistry) {
	// Register all models with their table names (without schema for SQLite compatibility)
	registry.RegisterModel("users", &models.ModelPublicUsers{})
	registry.RegisterModel("api_keys", &models.ModelPublicAPIKey{})
	registry.RegisterModel("hooks", &models.ModelPublicHook{})
	registry.RegisterModel("whatsapp_accounts", &models.ModelPublicWhatsappAccount{})
	registry.RegisterModel("event_logs", &models.ModelPublicEventLog{})
	registry.RegisterModel("sessions", &models.ModelPublicSession{})
	registry.RegisterModel("message_cache", &models.ModelPublicMessageCache{})
}

// SetupWhatsAppRoutes adds all WhatsApp API routes
func SetupWhatsAppRoutes(router *mux.Router, wh WhatsHookedInterface, distFS fs.FS) {
	h := wh.Handlers()

	// Landing page (no auth required)
	router.HandleFunc("/", h.ServeIndex).Methods("GET")

	// Privacy policy and terms of service (no auth required)
	router.HandleFunc("/privacy-policy", h.ServePrivacyPolicy).Methods("GET")
	router.HandleFunc("/terms-of-service", h.ServeTermsOfService).Methods("GET")

	// Logo files served from the embedded pkg/handlers/static/ (referenced by index.html)
	router.HandleFunc("/static/logo.png", h.ServeStatic)
	router.HandleFunc("/static/logo1024.png", h.ServeStatic)

	// Everything else under /static/ is served from the built web/public/ directory
	router.PathPrefix("/static/").Handler(http.StripPrefix("/static", http.FileServer(http.FS(distFS))))

	// Health check (no auth required)
	router.HandleFunc("/health", h.Health).Methods("GET")

	// Hook management (with auth)
	router.HandleFunc("/api/hooks", h.Auth(h.Hooks))
	router.HandleFunc("/api/hooks/add", h.Auth(h.AddHook)).Methods("POST")
	router.HandleFunc("/api/hooks/remove", h.Auth(h.RemoveHook)).Methods("POST")

	// Account management (with auth)
	router.HandleFunc("/api/accounts", h.Auth(h.Accounts))
	router.HandleFunc("/api/accounts/status", h.Auth(h.AccountStatuses)).Methods("GET")
	router.HandleFunc("/api/accounts/add", h.Auth(h.AddAccount)).Methods("POST")
	router.HandleFunc("/api/accounts/update", h.Auth(h.UpdateAccount)).Methods("POST")
	router.HandleFunc("/api/accounts/remove", h.Auth(h.RemoveAccount)).Methods("POST")
	router.HandleFunc("/api/accounts/disable", h.Auth(h.DisableAccount)).Methods("POST")
	router.HandleFunc("/api/accounts/enable", h.Auth(h.EnableAccount)).Methods("POST")

	// Send messages (with auth)
	router.HandleFunc("/api/send", h.Auth(h.SendMessage)).Methods("POST")
	router.HandleFunc("/api/send/image", h.Auth(h.SendImage)).Methods("POST")
	router.HandleFunc("/api/send/video", h.Auth(h.SendVideo)).Methods("POST")
	router.HandleFunc("/api/send/document", h.Auth(h.SendDocument)).Methods("POST")
	router.HandleFunc("/api/send/audio", h.Auth(h.SendAudio)).Methods("POST")
	router.HandleFunc("/api/send/sticker", h.Auth(h.SendSticker)).Methods("POST")
	router.HandleFunc("/api/send/location", h.Auth(h.SendLocation)).Methods("POST")
	router.HandleFunc("/api/send/contacts", h.Auth(h.SendContacts)).Methods("POST")
	router.HandleFunc("/api/send/interactive", h.Auth(h.SendInteractive)).Methods("POST")
	router.HandleFunc("/api/send/template", h.Auth(h.SendTemplate)).Methods("POST")
	router.HandleFunc("/api/send/flow", h.Auth(h.SendFlow)).Methods("POST")
	router.HandleFunc("/api/send/reaction", h.Auth(h.SendReaction)).Methods("POST")
	router.HandleFunc("/api/send/catalog", h.Auth(h.SendCatalogMessage)).Methods("POST")
	router.HandleFunc("/api/send/product", h.Auth(h.SendSingleProduct)).Methods("POST")
	router.HandleFunc("/api/send/product-list", h.Auth(h.SendProductList)).Methods("POST")

	// Message operations (with auth)
	router.HandleFunc("/api/messages/read", h.Auth(h.MarkAsRead)).Methods("POST")

	// Serve media files (with auth)
	router.PathPrefix("/api/media/").HandlerFunc(h.ServeMedia)

	// Serve QR codes (no auth - needed during pairing)
	router.PathPrefix("/api/qr/").HandlerFunc(h.ServeQRCode)

	// Business API webhooks (no auth - Meta validates via verify_token)
	router.PathPrefix("/webhooks/whatsapp/").HandlerFunc(h.BusinessAPIWebhook)

	// Template management (with auth)
	router.HandleFunc("/api/templates", h.Auth(h.ListTemplates))
	router.HandleFunc("/api/templates/upload", h.Auth(h.UploadTemplate)).Methods("POST")
	router.HandleFunc("/api/templates/delete", h.Auth(h.DeleteTemplate)).Methods("POST")

	// Flow management (with auth)
	router.HandleFunc("/api/flows", h.Auth(h.ListFlows))
	router.HandleFunc("/api/flows/create", h.Auth(h.CreateFlow)).Methods("POST")
	router.HandleFunc("/api/flows/get", h.Auth(h.GetFlow))
	router.HandleFunc("/api/flows/upload", h.Auth(h.UploadFlowAsset)).Methods("POST")
	router.HandleFunc("/api/flows/publish", h.Auth(h.PublishFlow)).Methods("POST")
	router.HandleFunc("/api/flows/deprecate", h.Auth(h.DeprecateFlow)).Methods("POST")
	router.HandleFunc("/api/flows/delete", h.Auth(h.DeleteFlow)).Methods("POST")

	// Phone number management (with auth)
	router.HandleFunc("/api/phone-numbers", h.Auth(h.ListPhoneNumbers))
	router.HandleFunc("/api/phone-numbers/request-code", h.Auth(h.RequestVerificationCode)).Methods("POST")
	router.HandleFunc("/api/phone-numbers/verify-code", h.Auth(h.VerifyCode)).Methods("POST")
	router.HandleFunc("/api/phone-numbers/register", h.Auth(h.RegisterPhoneNumber)).Methods("POST")

	// Media management (with auth)
	router.HandleFunc("/api/media/upload", h.Auth(h.UploadMedia)).Methods("POST")
	router.HandleFunc("/api/media-delete", h.Auth(h.DeleteMediaFile)).Methods("POST")

	// Business profile (with auth)
	router.HandleFunc("/api/business-profile", h.Auth(h.GetBusinessProfile))
	router.HandleFunc("/api/business-profile/update", h.Auth(h.UpdateBusinessProfile)).Methods("POST")

	// Catalog / commerce (with auth)
	router.HandleFunc("/api/catalogs", h.Auth(h.ListCatalogs))
	router.HandleFunc("/api/catalogs/products", h.Auth(h.ListProducts))

	// Message cache management (with auth)
	router.HandleFunc("/api/cache", h.Auth(h.GetCachedEvents)).Methods("GET")
	router.HandleFunc("/api/cache/stats", h.Auth(h.GetCacheStats)).Methods("GET")
	router.HandleFunc("/api/cache/replay", h.Auth(h.ReplayCachedEvents)).Methods("POST")
	router.HandleFunc("/api/cache/event", h.Auth(h.GetCachedEvent)).Methods("GET")
	router.HandleFunc("/api/cache/event/replay", h.Auth(h.ReplayCachedEvent)).Methods("POST")
	router.HandleFunc("/api/cache/event/delete", h.Auth(h.DeleteCachedEvent)).Methods("DELETE")
	router.HandleFunc("/api/cache/clear", h.Auth(h.ClearCache)).Methods("DELETE")
}

// SetupCustomRoutes adds custom authentication and management routes
func SetupCustomRoutes(router *mux.Router, secProvider security.SecurityProvider, db *bun.DB) {
	// Health check endpoint
	router.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(`{"status":"healthy"}`))
	}).Methods("GET")

	// Login endpoint
	router.HandleFunc("/api/v1/auth/login", func(w http.ResponseWriter, r *http.Request) {
		handleLogin(w, r, secProvider)
	}).Methods("POST", "OPTIONS")

	// Logout endpoint
	router.HandleFunc("/api/v1/auth/logout", func(w http.ResponseWriter, r *http.Request) {
		handleLogout(w, r, secProvider)
	}).Methods("POST", "OPTIONS")

	// Unified query endpoint for ResolveSpec-style queries
	router.HandleFunc("/api/v1/query", func(w http.ResponseWriter, r *http.Request) {
		handleQuery(w, r, db, secProvider)
	}).Methods("POST", "OPTIONS")
}

// handleLogin handles user login
func handleLogin(w http.ResponseWriter, r *http.Request, secProvider security.SecurityProvider) {
	var req security.LoginRequest
	if err := parseJSON(r, &req); err != nil {
		http.Error(w, "Invalid request", http.StatusBadRequest)
		return
	}

	resp, err := secProvider.Login(r.Context(), req)
	if err != nil {
		http.Error(w, "Invalid credentials", http.StatusUnauthorized)
		return
	}

	writeJSON(w, http.StatusOK, resp)
}

// handleLogout handles user logout
func handleLogout(w http.ResponseWriter, r *http.Request, secProvider security.SecurityProvider) {
	token := extractToken(r)
	if token == "" {
		http.Error(w, "No token provided", http.StatusBadRequest)
		return
	}

	req := security.LogoutRequest{Token: token}
	if err := secProvider.Logout(r.Context(), req); err != nil {
		http.Error(w, "Logout failed", http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, map[string]string{"message": "Logged out successfully"})
}

// QueryRequest represents a unified query request
type QueryRequest struct {
	Action        string                 `json:"action"`                   // "list", "get", "create", "update", "delete"
	Table         string                 `json:"table"`                    // Table name (e.g., "users", "hooks")
	ID            string                 `json:"id,omitempty"`             // For get/update/delete
	Data          map[string]interface{} `json:"data,omitempty"`           // For create/update
	Filters       map[string]interface{} `json:"filters,omitempty"`        // For list — exact match
	Search        string                 `json:"search,omitempty"`         // For list — LIKE across SearchColumns
	SearchColumns []string               `json:"search_columns,omitempty"` // Columns to apply Search against
	OrderBy       string                 `json:"order_by,omitempty"`       // Column to order by
	OrderDir      string                 `json:"order_dir,omitempty"`      // "ASC" or "DESC"
	Limit         int                    `json:"limit,omitempty"`
	Offset        int                    `json:"offset,omitempty"`
}

// handleQuery handles unified query requests
func handleQuery(w http.ResponseWriter, r *http.Request, db *bun.DB, secProvider security.SecurityProvider) {
	// Authenticate request
	userCtx, err := secProvider.Authenticate(r)
	if err != nil {
		http.Error(w, "Authentication required", http.StatusUnauthorized)
		return
	}

	var req QueryRequest
	if err := parseJSON(r, &req); err != nil {
		http.Error(w, "Invalid request body", http.StatusBadRequest)
		return
	}

	// Execute query based on action
	switch req.Action {
	case "list":
		handleQueryList(w, r, db, req, userCtx)
	case "get":
		handleQueryGet(w, r, db, req, userCtx)
	case "create":
		handleQueryCreate(w, r, db, req, userCtx)
	case "update":
		handleQueryUpdate(w, r, db, req, userCtx)
	case "delete":
		handleQueryDelete(w, r, db, req, userCtx)
	default:
		http.Error(w, "Invalid action", http.StatusBadRequest)
	}
}

// applySearchAndFilters applies exact-match filters and LIKE search to a select query.
func applySearchAndFilters(query *bun.SelectQuery, req QueryRequest) *bun.SelectQuery {
	for key, value := range req.Filters {
		query = query.Where("? = ?", bun.Ident(key), value)
	}
	if req.Search != "" && len(req.SearchColumns) > 0 {
		query = query.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery {
			for i, col := range req.SearchColumns {
				if i == 0 {
					q = q.Where("? LIKE ?", bun.Ident(col), "%"+req.Search+"%")
				} else {
					q = q.WhereOr("? LIKE ?", bun.Ident(col), "%"+req.Search+"%")
				}
			}
			return q
		})
	}
	return query
}

// handleQueryList lists records from a table
func handleQueryList(w http.ResponseWriter, r *http.Request, db *bun.DB, req QueryRequest, userCtx *security.UserContext) {
	registry := getModelForTable(req.Table)
	if registry == nil {
		http.Error(w, "Table not found", http.StatusNotFound)
		return
	}

	results := registry()
	query := db.NewSelect().Model(results)
	query = applySearchAndFilters(query, req)

	// Apply ordering
	if req.OrderBy != "" {
		dir := "ASC"
		if strings.ToUpper(req.OrderDir) == "DESC" {
			dir = "DESC"
		}
		query = query.OrderExpr("? "+dir, bun.Ident(req.OrderBy))
	}

	// Apply limit/offset
	if req.Limit > 0 {
		query = query.Limit(req.Limit)
	}
	if req.Offset > 0 {
		query = query.Offset(req.Offset)
	}

	if err := query.Scan(r.Context()); err != nil {
		http.Error(w, "Query failed", http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, results)
}

// handleQueryGet retrieves a single record by ID
func handleQueryGet(w http.ResponseWriter, r *http.Request, db *bun.DB, req QueryRequest, userCtx *security.UserContext) {
	model := getModelSingleForTable(req.Table)
	if model == nil {
		http.Error(w, "Table not found", http.StatusNotFound)
		return
	}

	err := db.NewSelect().Model(model).Where("id = ?", req.ID).Scan(r.Context())
	if err != nil {
		http.Error(w, "Record not found", http.StatusNotFound)
		return
	}

	writeJSON(w, http.StatusOK, model)
}

// handleQueryCreate creates a new record
func handleQueryCreate(w http.ResponseWriter, r *http.Request, db *bun.DB, req QueryRequest, userCtx *security.UserContext) {
	model := getModelSingleForTable(req.Table)
	if model == nil {
		http.Error(w, "Table not found", http.StatusNotFound)
		return
	}

	// Initialize data map if needed
	if req.Data == nil {
		req.Data = make(map[string]interface{})
	}

	// Auto-generate UUID for id field if not provided
	generatedID := ""
	if _, exists := req.Data["id"]; !exists {
		generatedID = uuid.New().String()
		req.Data["id"] = generatedID
	}

	// Auto-inject user_id for tables that need it
	if userCtx != nil && userCtx.Claims != nil {
		// Get user_id from claims (it's stored as UUID string)
		if userIDClaim, ok := userCtx.Claims["user_id"]; ok {
			if userID, ok := userIDClaim.(string); ok && userID != "" {
				// Add user_id to data if the table requires it and it's not already set
				tablesWithUserID := map[string]bool{
					"hooks":             true,
					"whatsapp_accounts": true,
					"api_keys":          true,
					"event_logs":        true,
				}

				if tablesWithUserID[req.Table] {
					// Only set user_id if not already provided
					if _, exists := req.Data["user_id"]; !exists {
						req.Data["user_id"] = userID
					}
				}
			}
		}
	}

	// Auto-generate session_path for WhatsApp accounts if not provided
	if req.Table == "whatsapp_accounts" {
		// Set session_path if not already provided
		if _, exists := req.Data["session_path"]; !exists {
			// Use account_id if provided, otherwise use generated id
			sessionID := ""
			if accountID, ok := req.Data["account_id"].(string); ok && accountID != "" {
				sessionID = accountID
			} else if generatedID != "" {
				sessionID = generatedID
			} else if id, ok := req.Data["id"].(string); ok && id != "" {
				sessionID = id
			}
			if sessionID != "" {
				req.Data["session_path"] = fmt.Sprintf("./sessions/%s", sessionID)
			}
		}
	}

	if req.Table == "users" {
		rawPassword, exists := req.Data["password"]
		if !exists {
			http.Error(w, "Password is required", http.StatusBadRequest)
			return
		}

		password, ok := rawPassword.(string)
		if !ok || password == "" {
			http.Error(w, "Password is required", http.StatusBadRequest)
			return
		}

		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
		if err != nil {
			http.Error(w, "Failed to process password", http.StatusInternalServerError)
			return
		}
		req.Data["password"] = string(hashedPassword)
	}

	// Convert data map to model using JSON marshaling
	dataJSON, err := json.Marshal(req.Data)
	if err != nil {
		http.Error(w, "Invalid data", http.StatusBadRequest)
		return
	}

	if err := json.Unmarshal(dataJSON, model); err != nil {
		http.Error(w, "Invalid data format", http.StatusBadRequest)
		return
	}

	// Ensure ID is set after unmarshaling by using model-specific handling
	if generatedID != "" {
		switch m := model.(type) {
		case *models.ModelPublicWhatsappAccount:
			m.ID.FromString(generatedID)
		case *models.ModelPublicHook:
			m.ID.FromString(generatedID)
		case *models.ModelPublicAPIKey:
			m.ID.FromString(generatedID)
		case *models.ModelPublicEventLog:
			m.ID.FromString(generatedID)
		case *models.ModelPublicUsers:
			m.ID.FromString(generatedID)
		}
	}

	// Insert into database
	_, err = db.NewInsert().Model(model).Exec(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Create failed: %v", err), http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusCreated, model)
}

// handleQueryUpdate updates an existing record
func handleQueryUpdate(w http.ResponseWriter, r *http.Request, db *bun.DB, req QueryRequest, userCtx *security.UserContext) {
	model := getModelSingleForTable(req.Table)
	if model == nil {
		http.Error(w, "Table not found", http.StatusNotFound)
		return
	}

	if req.Data == nil || len(req.Data) == 0 {
		http.Error(w, "No update data provided", http.StatusBadRequest)
		return
	}

	if req.Table == "users" {
		if rawPassword, exists := req.Data["password"]; exists {
			password, ok := rawPassword.(string)
			if !ok {
				http.Error(w, "Invalid password format", http.StatusBadRequest)
				return
			}

			if password == "" {
				delete(req.Data, "password")
			} else {
				hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
				if err != nil {
					http.Error(w, "Failed to process password", http.StatusInternalServerError)
					return
				}
				req.Data["password"] = string(hashedPassword)
			}
		}
	}

	updateQuery := db.NewUpdate().Model(model).Where("id = ?", req.ID)
	updatedColumns := 0

	for column, value := range req.Data {
		// Protect immutable/audit columns from accidental overwrite.
		if column == "id" || column == "created_at" {
			continue
		}
		updateQuery = updateQuery.Set("? = ?", bun.Ident(column), value)
		updatedColumns++
	}

	if updatedColumns == 0 {
		http.Error(w, "No mutable fields to update", http.StatusBadRequest)
		return
	}

	// Update only the provided fields.
	_, err := updateQuery.Exec(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Update failed: %v", err), http.StatusInternalServerError)
		return
	}

	// Return the latest database row after update.
	if err := db.NewSelect().Model(model).Where("id = ?", req.ID).Scan(r.Context()); err != nil {
		http.Error(w, fmt.Sprintf("Update succeeded but reload failed: %v", err), http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, model)
}

// handleQueryDelete deletes a record
func handleQueryDelete(w http.ResponseWriter, r *http.Request, db *bun.DB, req QueryRequest, userCtx *security.UserContext) {
	model := getModelSingleForTable(req.Table)
	if model == nil {
		http.Error(w, "Table not found", http.StatusNotFound)
		return
	}

	_, err := db.NewDelete().Model(model).Where("id = ?", req.ID).Exec(r.Context())
	if err != nil {
		http.Error(w, fmt.Sprintf("Delete failed: %v", err), http.StatusInternalServerError)
		return
	}

	writeJSON(w, http.StatusOK, map[string]string{"message": "Deleted successfully"})
}

// getModelForTable returns a function that creates a slice of models for the table
func getModelForTable(table string) func() interface{} {
	switch table {
	case "users":
		return func() interface{} { return &[]models.ModelPublicUsers{} }
	case "hooks":
		return func() interface{} { return &[]models.ModelPublicHook{} }
	case "whatsapp_accounts":
		return func() interface{} { return &[]models.ModelPublicWhatsappAccount{} }
	case "event_logs":
		return func() interface{} { return &[]models.ModelPublicEventLog{} }
	case "api_keys":
		return func() interface{} { return &[]models.ModelPublicAPIKey{} }
	case "sessions":
		return func() interface{} { return &[]models.ModelPublicSession{} }
	case "message_cache":
		return func() interface{} { return &[]models.ModelPublicMessageCache{} }
	default:
		return nil
	}
}

// getModelSingleForTable returns a single model instance for the table
func getModelSingleForTable(table string) interface{} {
	switch table {
	case "users":
		return &models.ModelPublicUsers{}
	case "hooks":
		return &models.ModelPublicHook{}
	case "whatsapp_accounts":
		return &models.ModelPublicWhatsappAccount{}
	case "event_logs":
		return &models.ModelPublicEventLog{}
	case "api_keys":
		return &models.ModelPublicAPIKey{}
	case "sessions":
		return &models.ModelPublicSession{}
	case "message_cache":
		return &models.ModelPublicMessageCache{}
	default:
		return nil
	}
}

// Helper functions
func parseJSON(r *http.Request, v interface{}) error {
	decoder := json.NewDecoder(r.Body)
	return decoder.Decode(v)
}

func writeJSON(w http.ResponseWriter, status int, v interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	json.NewEncoder(w).Encode(v)
}

func extractToken(r *http.Request) string {
	// Extract from Authorization header: "Bearer <token>"
	auth := r.Header.Get("Authorization")
	if len(auth) > 7 && auth[:7] == "Bearer " {
		return auth[7:]
	}
	return ""
}

// embeddedSPAHandler serves a React SPA from an embedded fs.FS.
// Static assets are served directly; all other paths fall back to index.html
// to support client-side routing.
type embeddedSPAHandler struct {
	fs        fs.FS
	indexPath string
}

func (h embeddedSPAHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	path := r.URL.Path
	// Strip leading slash so fs.FS Open calls work correctly.
	if len(path) > 0 && path[0] == '/' {
		path = path[1:]
	}

	if path == "" {
		path = h.indexPath
	}

	// Try to open the requested path in the embedded FS.
	f, err := h.fs.Open(path)
	if err != nil {
		// Not found — serve index.html for client-side routing.
		r2 := r.Clone(r.Context())
		r2.URL.Path = "/" + h.indexPath
		http.FileServer(http.FS(h.fs)).ServeHTTP(w, r2)
		return
	}
	defer f.Close()

	stat, err := f.Stat()
	if err != nil || stat.IsDir() {
		r2 := r.Clone(r.Context())
		r2.URL.Path = "/" + h.indexPath
		http.FileServer(http.FS(h.fs)).ServeHTTP(w, r2)
		return
	}

	// Serve the real file.
	http.FileServer(http.FS(h.fs)).ServeHTTP(w, r)
}

// corsMiddleware adds CORS headers to allow frontend requests
func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Allow requests from any origin (adjust in production)
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With")
		w.Header().Set("Access-Control-Allow-Credentials", "true")
		w.Header().Set("Access-Control-Max-Age", "86400")

		// Handle preflight requests
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}
