package main

import (
	"context"
	"encoding/json"
	"log"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/gorilla/websocket"
	"github.com/joho/godotenv"
)

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

type Client struct {
	conn *websocket.Conn
	mu   sync.Mutex
}

type Hub struct {
	clients   map[*Client]bool
	broadcast chan []byte
	mu        sync.Mutex
}

func newHub() *Hub {
	return &Hub{
		clients:   make(map[*Client]bool),
		broadcast: make(chan []byte, 512),
	}
}

func (h *Hub) run() {
	for msg := range h.broadcast {
		h.mu.Lock()
		var dead []*Client
		for client := range h.clients {
			if err := client.writeMessage(websocket.TextMessage, msg); err != nil {
				dead = append(dead, client)
			}
		}
		for _, c := range dead {
			delete(h.clients, c)
			c.conn.Close()
		}
		h.mu.Unlock()
	}
}

func (c *Client) writeMessage(msgType int, data []byte) error {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.conn.WriteMessage(msgType, data)
}

func (h *Hub) wsHandler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		return
	}
	client := &Client{conn: conn}
	h.mu.Lock()
	h.clients[client] = true
	h.mu.Unlock()

	defer func() {
		h.mu.Lock()
		delete(h.clients, client)
		h.mu.Unlock()
		conn.Close()
	}()

	go func() {
		t := time.NewTicker(25 * time.Second)
		defer t.Stop()
		for range t.C {
			if err := client.writeMessage(websocket.PingMessage, nil); err != nil {
				return
			}
		}
	}()

	for {
		if _, _, err := conn.ReadMessage(); err != nil {
			break
		}
	}
}

func main() {
	godotenv.Load()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	db, dbErr := initDB(ctx)
	if dbErr != nil {
		log.Printf("WARNING: PostgreSQL unavailable (%v) — running read-only from Redis", dbErr)
	}
	if db != nil {
		defer db.Close()
		// Apply migrations
		if err := runMigrations(ctx, db.pool); err != nil {
			log.Printf("WARNING: Migrations failed (%v)", err)
		}
	}

	// Redis disabled - will enable later
	// rdb := newRedisClient()
	// hub := newHub()
	// go hub.run()
	// go pollRedis(ctx, rdb, db, hub)

	// Start Redis ingestion worker (disabled - requires migrations)
	// if db != nil {
	// 	startRedisIngestionWorker(db, 30*time.Second)
	// }

	// Seed default users disabled - users imported from database
	// if db != nil {
	// 	db.seedDefaultUsers(ctx)
	// }

	mux := http.NewServeMux()

	// Seed domain systems disabled - requires migrations
	// if db != nil {
	// 	db.seedDomainSystems(ctx)
	// }

	// Public endpoints (no auth)
	mux.HandleFunc("/health", cors(func(w http.ResponseWriter, r *http.Request) {
		json.NewEncoder(w).Encode(map[string]any{
			"status": "ok",
			"time":   time.Now().UTC().Format(time.RFC3339),
		})
	}))

	// WebSocket disabled - Redis not available
	// mux.HandleFunc("/ws", hub.wsHandler)

	// Protected API endpoints
	mux.HandleFunc("/api/telemetry/latest",  cors(authMiddleware(latestHandler(db))))
	mux.HandleFunc("/api/telemetry/history", cors(authMiddleware(historyHandler(db))))
	mux.HandleFunc("/api/alarms",            cors(authMiddleware(alarmsHandler(db))))
	mux.HandleFunc("/api/stats",             cors(authMiddleware(statsHandler(db))))

	// Admin — user management (admin role required)
	mux.HandleFunc("GET /api/admin/users",                      cors(adminOnly(listUsersHandler(db))))
	mux.HandleFunc("POST /api/admin/users",                     cors(adminOnly(createUserHandler(db))))
	mux.HandleFunc("PUT /api/admin/users/{id}",                 cors(adminOnly(updateUserHandler(db))))
	mux.HandleFunc("DELETE /api/admin/users/{id}",              cors(adminOnly(deleteUserHandler(db))))
	mux.HandleFunc("POST /api/admin/users/{id}/reset-password", cors(adminOnly(resetPasswordHandler(db))))

	// Admin -- System Menu management (admin only)
	mux.HandleFunc("GET /api/admin/menu",        cors(adminOnly(listMenuHandler(db))))
	mux.HandleFunc("POST /api/admin/menu",       cors(adminOnly(createMenuHandler(db))))
	mux.HandleFunc("GET /api/admin/menu/{id}",   cors(adminOnly(getMenuHandler(db))))
	mux.HandleFunc("PUT /api/admin/menu/{id}",   cors(adminOnly(updateMenuHandler(db))))
	mux.HandleFunc("DELETE /api/admin/menu/{id}", cors(adminOnly(deleteMenuHandler(db))))

	// Admin -- CMMS bulk import (admin only)
	mux.HandleFunc("POST /api/admin/cmms/import",   cors(adminOnly(importCMMSAssetsHandler(db))))
	mux.HandleFunc("POST /api/admin/cmms/sync",     cors(adminOnly(syncInnomaintAssetsHandler(db))))
	mux.HandleFunc("POST /api/admin/cmms/sync/retry-failed", cors(adminOnly(retryFailedSyncHandler(db))))
	mux.HandleFunc("GET /api/admin/cmms/sync/status", cors(adminOnly(syncStatusHandler(db))))
	mux.HandleFunc("POST /api/admin/cmms/sync/{id}",cors(adminOnly(syncSingleAssetHandler(db))))
	mux.HandleFunc("POST /api/admin/assets",         cors(adminOnly(createAssetHandler(db))))
	mux.HandleFunc("PUT /api/admin/assets/{id}",     cors(adminOnly(updateAssetHandler(db))))
	mux.HandleFunc("DELETE /api/admin/assets/{id}",  cors(adminOnly(deleteAssetHandler(db))))

	// Layer 01: Data & Field Intelligence — Real Content APIs
	if db != nil {
		mux.HandleFunc("GET /api/v1/layer01/valves",          cors(authMiddleware(GetValves(db))))
		mux.HandleFunc("GET /api/v1/layer01/pumps",           cors(authMiddleware(GetPumps(db))))
		mux.HandleFunc("GET /api/v1/layer01/flow-meters",     cors(authMiddleware(GetFlowMeters(db))))
		mux.HandleFunc("GET /api/v1/layer01/pressure-sensors", cors(authMiddleware(GetPressureSensors(db))))
		mux.HandleFunc("GET /api/v1/layer01/level-sensors",   cors(authMiddleware(GetLevelSensors(db))))
		mux.HandleFunc("GET /api/v1/layer01/dma-registry",    cors(authMiddleware(GetDMARegistry(db))))
		mux.HandleFunc("GET /api/v1/layer01/health-scores",   cors(authMiddleware(GetHealthScores(db))))
		mux.HandleFunc("GET /api/v1/layer01/live-readings",   cors(authMiddleware(GetLiveReadings(db))))
		mux.HandleFunc("GET /api/v1/layer01/manual-entries",  cors(authMiddleware(GetManualEntries(db))))
		mux.HandleFunc("GET /api/v1/layer01/realtime-summary",   cors(authMiddleware(GetRealtimeSummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/meter-reads",     cors(authMiddleware(GetMeterReads(db))))

		// Pipe Network Registry APIs
		mux.HandleFunc("GET /api/v1/layer01/pipe-network/summary",          cors(authMiddleware(GetPipeNetworkSummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/pipe-network/by-diameter",     cors(authMiddleware(GetPipesByDiameter(db))))
		mux.HandleFunc("GET /api/v1/layer01/pipe-network/by-material",     cors(authMiddleware(GetPipesByMaterial(db))))
		mux.HandleFunc("GET /api/v1/layer01/pipe-network/by-age",          cors(authMiddleware(GetPipesByAge(db))))
		mux.HandleFunc("GET /api/v1/layer01/pipe-network/maintenance-history", cors(authMiddleware(GetMaintenanceHistory(db))))
		mux.HandleFunc("GET /api/v1/layer01/pipe-network/health-by-location",  cors(authMiddleware(GetNetworkHealthByLocation(db))))

		// Pressurized Supply Module APIs
		mux.HandleFunc("GET /api/v1/pressurized-supply/summary",       cors(authMiddleware(GetSupplySummary(db))))
		mux.HandleFunc("GET /api/v1/pressurized-supply/zones",         cors(authMiddleware(GetPressureZones(db))))
		mux.HandleFunc("GET /api/v1/pressurized-supply/pump-stations", cors(authMiddleware(GetPumpStations(db))))
		mux.HandleFunc("GET /api/v1/pressurized-supply/metrics",       cors(authMiddleware(GetSupplyMetrics(db))))
		mux.HandleFunc("GET /api/v1/pressurized-supply/connections",   cors(authMiddleware(GetServiceConnections(db))))
		mux.HandleFunc("GET /api/v1/pressurized-supply/maintenance",   cors(authMiddleware(GetSupplyMaintenance(db))))

		// IoT Devices APIs (Field Instrumentation / IoT)
		mux.HandleFunc("GET /api/v1/iot/flow-meters",        cors(authMiddleware(GetIoTFlowMeters(db))))
		mux.HandleFunc("GET /api/v1/iot/pressure-sensors",   cors(authMiddleware(GetIoTPressureSensors(db))))
		mux.HandleFunc("GET /api/v1/iot/reservoir-sensors",  cors(authMiddleware(GetIoTReservoirSensors(db))))
		mux.HandleFunc("GET /api/v1/iot/smart-meters",       cors(authMiddleware(GetIoTSmartMeters(db))))
		mux.HandleFunc("GET /api/v1/iot/water-quality",      cors(authMiddleware(GetIoTWaterQualityProbes(db))))

		// Data & Field Intelligence APIs (Layer 01 Aggregation)
		mux.HandleFunc("GET /api/v1/layer01/data-field-intelligence/summary", cors(authMiddleware(GetDataFieldIntelligenceSummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/network-metrics",                  cors(authMiddleware(GetNetworkMetrics(db))))

		// Valve & Hydrant Registry APIs
		mux.HandleFunc("GET /api/v1/layer01/valves/stats",           cors(authMiddleware(GetValveStats(db))))
		mux.HandleFunc("GET /api/v1/layer01/valves/by-type",         cors(authMiddleware(GetValvesByType(db))))
		mux.HandleFunc("GET /api/v1/layer01/valves/by-size",         cors(authMiddleware(GetValvesBySize(db))))
		mux.HandleFunc("GET /api/v1/layer01/valves/health",          cors(authMiddleware(GetValveHealth(db))))
		mux.HandleFunc("GET /api/v1/layer01/hydrants/stats",         cors(authMiddleware(GetHydrantStats(db))))
		mux.HandleFunc("GET /api/v1/layer01/hydrants/by-type",       cors(authMiddleware(GetHydrantsByType(db))))
		mux.HandleFunc("GET /api/v1/layer01/hydrants/health",        cors(authMiddleware(GetHydrantHealth(db))))

		// Reservoirs / OHT / GLR / Sumps APIs
		mux.HandleFunc("GET /api/v1/layer01/reservoirs/summary", cors(authMiddleware(GetReservoirSummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/reservoirs/by-location", cors(authMiddleware(GetReservoirByLocation(db))))
		mux.HandleFunc("GET /api/v1/layer01/reservoirs/health", cors(authMiddleware(GetReservoirHealth(db))))

		// Pumping Stations APIs
		mux.HandleFunc("GET /api/v1/layer01/pumping-stations/summary", cors(authMiddleware(GetPumpingStationSummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/pumping-stations/list",    cors(authMiddleware(GetPumpingStations(db))))

		// Meters (Bulk & Consumer) APIs
		mux.HandleFunc("GET /api/v1/layer01/meters/summary",   cors(authMiddleware(GetMeterSummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/meters/by-type",   cors(authMiddleware(GetMetersByType(db))))

		// DMA Registry APIs
		mux.HandleFunc("GET /api/v1/layer01/dma/summary", cors(authMiddleware(GetDMASummary(db))))
		mux.HandleFunc("GET /api/v1/layer01/dma/list",    cors(authMiddleware(GetDMAList(db))))
	}

	// Location endpoints
	mux.HandleFunc("GET /api/locations",             cors(authMiddleware(listLocationsHandler(db))))
	mux.HandleFunc("GET /api/locations/tree",         cors(authMiddleware(locationTreeHandler(db))))
	mux.HandleFunc("POST /api/admin/locations",       cors(adminOnly(createLocationHandler(db))))
	mux.HandleFunc("PUT /api/admin/locations/{id}",   cors(adminOnly(updateLocationHandler(db))))
	mux.HandleFunc("DELETE /api/admin/locations/{id}", cors(adminOnly(deleteLocationHandler(db))))

	// Scope dropdown endpoints (ic3_customer_master / ic3_location_master / ic3_system_master)
	// Public endpoints — no auth required for dropdown data
	mux.HandleFunc("GET /api/scope/customers",               cors(listScopeCustomersHandler(db)))
	mux.HandleFunc("GET /api/scope/customers-by-city",       cors(listCustomersByCityHandler(db)))
	mux.HandleFunc("GET /api/scope/locations",               cors(listScopeLocationsHandler(db)))
	mux.HandleFunc("GET /api/scope/systems",                 cors(listScopeSystemsHandler(db)))
	mux.HandleFunc("GET /api/scope/ic3-systems",             cors(listAllIC3SystemsHandler(db)))
	mux.HandleFunc("GET /api/scope/solution-scenarios",      cors(listSolutionScenariosHandler(db)))
	mux.HandleFunc("GET /api/scope/cities",                  cors(listScopeCitiesHandler(db)))
	mux.HandleFunc("GET /api/scope/customer-locations",      cors(listScopeCustomerLocationsHandler(db)))
	mux.HandleFunc("GET /api/scope/filter-asset-categories", cors(listFilteredAssetCategoriesHandler(db)))
	mux.HandleFunc("GET /api/scope/filter-asset-groups",     cors(listFilteredAssetGroupsHandler(db)))

	// Navigation menu endpoints
	mux.HandleFunc("GET /api/nav", cors(getNavMenuHandler(db)))
	mux.HandleFunc("GET /api/system-layers/{systemId}", cors(authMiddleware(getSystemLayersHandler(db))))

	// Customer endpoints
	mux.HandleFunc("GET /api/customers",                         cors(authMiddleware(listCustomersHandler(db))))
	mux.HandleFunc("GET /api/customers/{id}/connections",        cors(authMiddleware(listConnectionsHandler(db))))
	mux.HandleFunc("POST /api/admin/customers",                  cors(adminOnly(createCustomerHandler(db))))
	mux.HandleFunc("PUT /api/admin/customers/{id}",              cors(adminOnly(updateCustomerHandler(db))))
	mux.HandleFunc("DELETE /api/admin/customers/{id}",           cors(adminOnly(deleteCustomerHandler(db))))
	mux.HandleFunc("POST /api/admin/customers/{id}/connections", cors(adminOnly(createConnectionHandler(db))))

	// CMMS read endpoints (any authenticated user)
	mux.HandleFunc("GET /api/cmms/assets",      cors(authMiddleware(listCMMSAssetsHandler(db))))
	mux.HandleFunc("GET /api/cmms/assets/{id}", cors(authMiddleware(getCMMSAssetHandler(db))))
	mux.HandleFunc("GET /api/cmms/health",      cors(authMiddleware(cmmsDashboardHandler(db))))
	mux.HandleFunc("GET /api/cmms/pm-overdue",  cors(authMiddleware(cmmsPMOverdueHandler(db))))
	mux.HandleFunc("GET /api/cmms/sync-log",    cors(adminOnly(cmmsSyncLogHandler(db))))

	// GIS endpoints — real GPS from tbl_asset_location / tbl_zone / tbl_site
	mux.HandleFunc("GET /api/gis/assets",                      cors(listGISAssetsHandler(db)))
	mux.HandleFunc("GET /api/gis/sites",                       cors(listGISSiteClustersHandler(db)))
	mux.HandleFunc("GET /api/gis/asset-detail",                cors(getGISAssetDetailHandler(db)))
	mux.HandleFunc("GET /api/gis/asset-group-markers",         cors(authMiddleware(assetGroupMarkersHandler(db))))
	mux.HandleFunc("POST /api/admin/gis/generate-coordinates", cors(adminOnly(generateCoordinatesHandler(db))))

	// Asset dashboard — categories, groups, GIS assets by scope
	mux.HandleFunc("GET /api/assets/dashboard", cors(authMiddleware(assetDashboardHandler(db))))

	// AI Analytics — Anomaly Detection endpoints (auth optional for testing)
	mux.HandleFunc("POST /api/ai/anomalies",          cors(createAnomalyHandler(db)))
	mux.HandleFunc("GET /api/ai/anomalies/active",    cors(getActiveAnomaliesHandler(db)))
	mux.HandleFunc("GET /api/ai/anomalies/{id}",      cors(getAnomalyDetailHandler(db)))
	mux.HandleFunc("GET /api/ai/anomalies/history",   cors(getAnomalyHistoryHandler(db)))
	mux.HandleFunc("GET /api/ai/anomalies/metrics",   cors(getAnomalyMetricsHandler(db)))
	mux.HandleFunc("POST /api/ai/anomalies/{id}/dismiss", cors(dismissAnomalyHandler(db)))
	mux.HandleFunc("GET /api/ai/alert-rules",         cors(getAlertRulesHandler(db)))
	mux.HandleFunc("POST /api/ai/alert-rules",        cors(createAlertRuleHandler(db)))
	mux.HandleFunc("GET /api/ai/sensors/health",      cors(getSensorHealthHandler(db)))
	mux.HandleFunc("POST /api/work-orders",           cors(createWorkOrderHandler(db)))

	// AI Analytics — Pump Health endpoints (disabled - Redis not available)
	// mux.HandleFunc("GET /api/v1/health/assets",       cors(getAssetsHealthHandler(db, rdb)))
	// mux.HandleFunc("GET /api/v1/health/asset/{id}",   cors(getAssetHealthHandler(db, rdb)))

	// AI Analytics — NRW Engine endpoints (disabled - Redis not available)
	// mux.HandleFunc("GET /api/v1/nrw/live",            cors(getNRWLiveHandler(db, rdb)))
	mux.HandleFunc("GET /api/v1/nrw/{dma_id}/history", cors(getNRWHistoryHandler(db)))

	// AI Models endpoints
	mux.HandleFunc("GET /api/ai/models",               cors(authMiddleware(db.GetAIModelsHandler)))
	mux.HandleFunc("GET /api/ai/models/detail",        cors(authMiddleware(db.GetAIModelByCodeHandler)))
	mux.HandleFunc("GET /api/ai/models/metrics",       cors(authMiddleware(db.GetAIModelMetricsHandler)))
	mux.HandleFunc("GET /api/ai/models/predictions",   cors(authMiddleware(db.GetAIModelPredictionsHandler)))
	mux.HandleFunc("POST /api/ai/models/config",       cors(authMiddleware(db.UpdateAIModelConfigHandler)))

	// Authentication endpoints
	mux.HandleFunc("POST /api/auth/login",             cors(db.LoginHandler))
	mux.HandleFunc("GET /api/auth/me",                 cors(authMiddleware(db.GetCurrentUserHandler)))
	mux.HandleFunc("POST /api/auth/logout",            cors(authMiddleware(db.LogoutHandler)))
	mux.HandleFunc("GET /api/auth/users",              cors(authMiddleware(db.GetUsersHandler)))

	// GIS data endpoint
	mux.HandleFunc("GET /ic3/gis-data/gis.json", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.Header().Set("Cache-Control", "public, max-age=3600")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		http.ServeFile(w, r, "gis-data.json")
	})

	port := os.Getenv("PORT")
	if port == "" {
		port = "9090"
	}

	// Start continuous sync worker if DB is available
	// Start continuous sync worker only if explicitly enabled
	if db != nil && os.Getenv("ENABLE_ASSET_SYNC") == "true" {
		db.StartContinuousSyncWorker(ctx, 500, 5)
	}

	srv := &http.Server{Addr: ":" + port, Handler: globalCORS(mux)}
	log.Printf("IC³ Dashboard backend → http://localhost:%s", port)

	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("server: %v", err)
		}
	}()

	// Start AI workers (disabled - requires migrations)
	// if db != nil {
	// 	go RunAnomalyDetectionWorker(ctx, db, rdb)
	// 	go RunPumpHealthWorker(ctx, db, rdb)
	// 	go RunNRWEngine(ctx, db, rdb)
	// }

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
	<-sig
	log.Println("shutting down…")
	ctx2, c2 := context.WithTimeout(context.Background(), 5*time.Second)
	defer c2()
	srv.Shutdown(ctx2)
}

// globalCORS wraps the entire mux so every OPTIONS preflight is answered
// before routing, avoiding Go 1.22 pattern-conflict panics.
func globalCORS(h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
		if r.Method == http.MethodOptions {
			w.WriteHeader(http.StatusNoContent)
			return
		}
		h.ServeHTTP(w, r)
	})
}

func cors(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
		w.Header().Set("Content-Type", "application/json")
		if r.Method == http.MethodOptions {
			w.WriteHeader(204)
			return
		}
		next(w, r)
	}
}
