package main

import (
	"embed"
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/fs"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
	"github.com/gorilla/mux"
	"github.com/gorilla/websocket"
	log "github.com/sirupsen/logrus"
)

//go:embed frontend/dist/*
var frontendFiles embed.FS
var httpServerInstance *HTTPServer

type HTTPServer struct {
	Router          *mux.Router
	FlexRepo        *FlexDXClusterRepository
	ContactRepo     *Log4OMContactsRepository
	TCPServer       *TCPServer
	TCPClient       *TCPClient
	FlexClient      *FlexClient
	Port            string
	Log             *log.Logger
	lastQSOCount    int
	lastBandOpening map[string]time.Time
	statsCache      Stats
	statsMutex      sync.RWMutex
	lastUpdate      time.Time
	wsClients       map[*websocket.Conn]bool
	wsMutex         sync.RWMutex
	broadcast       chan WSMessage
	Watchlist       *Watchlist
}

type Stats struct {
	TotalSpots       int     `json:"totalSpots"`
	NewDXCC          int     `json:"newDXCC"`
	ConnectedClients int     `json:"connectedClients"`
	TotalContacts    int     `json:"totalContacts"`
	ClusterStatus    string  `json:"clusterStatus"`
	FlexStatus       string  `json:"flexStatus"`
	MyCallsign       string  `json:"myCallsign"`
	Mode             string  `json:"mode"`
	Filters          Filters `json:"filters"`
	SpotsReceived    int64   `json:"spotsReceived"`
	SpotsProcessed   int64   `json:"spotsProcessed"`
	SpotsRejected    int64   `json:"spotsRejected"`
	SpotSuccessRate  float64 `json:"spotSuccessRate"`
}

type Filters struct {
	Skimmer bool `json:"skimmer"`
	FT8     bool `json:"ft8"`
	FT4     bool `json:"ft4"`
	Beacon  bool `json:"beacon"`
}

type APIResponse struct {
	Success bool        `json:"success"`
	Data    interface{} `json:"data,omitempty"`
	Error   string      `json:"error,omitempty"`
	Message string      `json:"message,omitempty"`
}

type WSMessage struct {
	Type string      `json:"type"`
	Data interface{} `json:"data"`
}

type SendCallsignRequest struct {
	Callsign string `json:"callsign"`
}

type WatchlistSpot struct {
	DX              string `json:"dx"`
	FrequencyMhz    string `json:"frequencyMhz"`
	Band            string `json:"band"`
	Mode            string `json:"mode"`
	SpotterCallsign string `json:"spotterCallsign"`
	UTCTime         string `json:"utcTime"`
	CountryName     string `json:"countryName"`
	NewDXCC         bool   `json:"newDXCC"`
	NewBand         bool   `json:"newBand"`
	NewMode         bool   `json:"newMode"`
	Worked          bool   `json:"worked"`
	WorkedBandMode  bool   `json:"workedBandMode"`
}

type RemoteControlRequestFreq struct {
	XMLName              xml.Name `xml:"RemoteControlRequest"`
	MessageId            string   `xml:"MessageId"`
	RemoteControlMessage string   `xml:"RemoteControlMessage"`
	Frequency            string   `xml:"Frequency"`
}

type RemoteControlRequestMode struct {
	XMLName              xml.Name `xml:"RemoteControlRequest"`
	MessageId            string   `xml:"MessageId"`
	RemoteControlMessage string   `xml:"RemoteControlMessage"`
	Mode                 string   `xml:"Mode"`
}

var upgrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true // Allow all origins in development
	},
}

func NewHTTPServer(flexRepo *FlexDXClusterRepository, contactRepo *Log4OMContactsRepository,
	tcpServer *TCPServer, tcpClient *TCPClient, flexClient *FlexClient, port string) *HTTPServer {

	server := &HTTPServer{
		Router:          mux.NewRouter(),
		FlexRepo:        flexRepo,
		ContactRepo:     contactRepo,
		TCPServer:       tcpServer,
		TCPClient:       tcpClient,
		FlexClient:      flexClient,
		Port:            port,
		Log:             Log,
		wsClients:       make(map[*websocket.Conn]bool),
		broadcast:       make(chan WSMessage, 256),
		Watchlist:       NewWatchlist("watchlist.json"),
		lastQSOCount:    0,
		lastBandOpening: make(map[string]time.Time),
	}

	httpServerInstance = server

	server.setupRoutes()
	go server.handleBroadcasts()
	go server.broadcastUpdates()

	return server
}

func (s *HTTPServer) setupRoutes() {
	// Enable CORS
	s.Router.Use(s.corsMiddleware)

	// API Routes
	api := s.Router.PathPrefix("/api").Subrouter()

	api.HandleFunc("/stats", s.getStats).Methods("GET", "OPTIONS")
	api.HandleFunc("/spots", s.getSpots).Methods("GET", "OPTIONS")
	api.HandleFunc("/spots/{id}", s.getSpotByID).Methods("GET", "OPTIONS")
	api.HandleFunc("/contacts", s.getContacts).Methods("GET", "OPTIONS")
	api.HandleFunc("/filters", s.updateFilters).Methods("POST", "OPTIONS")
	api.HandleFunc("/shutdown", s.shutdownApp).Methods("POST", "OPTIONS")
	api.HandleFunc("/send-callsign", s.handleSendCallsign).Methods("POST", "OPTIONS")
	api.HandleFunc("/watchlist", s.getWatchlist).Methods("GET", "OPTIONS")
	api.HandleFunc("/solar", s.HandleSolarData).Methods("GET", "OPTIONS")
	api.HandleFunc("/log/recent", s.getRecentQSOs).Methods("GET", "OPTIONS")
	api.HandleFunc("/log/stats", s.getLogStats).Methods("GET", "OPTIONS")
	api.HandleFunc("/log/dxcc-progress", s.getDXCCProgress).Methods("GET", "OPTIONS")
	api.HandleFunc("/watchlist/spots", s.getWatchlistSpotsWithStatus).Methods("GET", "OPTIONS")
	api.HandleFunc("/watchlist/add", s.addToWatchlist).Methods("POST", "OPTIONS")
	api.HandleFunc("/watchlist/remove", s.removeFromWatchlist).Methods("DELETE", "OPTIONS")
	api.HandleFunc("/watchlist/update-sound", s.updateWatchlistSound).Methods("POST", "OPTIONS")
	api.HandleFunc("/stats/spots", s.getSpotProcessingStats).Methods("GET", "OPTIONS")
	api.HandleFunc("/logs", s.getLogs).Methods("GET", "OPTIONS")

	// WebSocket endpoint
	api.HandleFunc("/ws", s.handleWebSocket).Methods("GET")

	s.setupStaticFiles()
}

func (s *HTTPServer) setupStaticFiles() {
	// Obtenir le sous-système de fichiers depuis dist/
	distFS, err := fs.Sub(frontendFiles, "frontend/dist")
	if err != nil {
		s.Log.Fatal("Cannot load frontend files:", err)
	}

	spaHandler := http.FileServer(http.FS(distFS))
	s.Router.PathPrefix("/").Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		path := r.URL.Path

		// Vérifier si le fichier existe
		if path != "/" {
			file, err := distFS.Open(strings.TrimPrefix(path, "/"))
			if err == nil {
				file.Close()
				spaHandler.ServeHTTP(w, r)
				return
			}
		}

		// Si pas trouvé ou racine, servir index.html
		r.URL.Path = "/"
		spaHandler.ServeHTTP(w, r)
	}))
}

func (s *HTTPServer) handleWebSocket(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		s.Log.Errorf("WebSocket upgrade failed: %v", err)
		return
	}

	s.wsMutex.Lock()
	s.wsClients[conn] = true
	clientCount := len(s.wsClients)
	s.wsMutex.Unlock()

	s.Log.Infof("New WebSocket client connected (total: %d)", clientCount)

	// Send initial data
	s.sendInitialData(conn)

	// Keep connection alive and handle client messages
	go s.handleWebSocketClient(conn)
}

func (s *HTTPServer) handleWebSocketClient(conn *websocket.Conn) {
	defer func() {
		s.wsMutex.Lock()
		delete(s.wsClients, conn)
		clientCount := len(s.wsClients)
		s.wsMutex.Unlock()

		conn.Close()
		s.Log.Infof("WebSocket client disconnected (remaining: %d)", clientCount)
	}()

	// Read messages from client (for ping/pong)
	for {
		_, _, err := conn.ReadMessage()
		if err != nil {
			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
				s.Log.Errorf("WebSocket error: %v", err)
			}
			break
		}
	}
}

func (s *HTTPServer) sendInitialData(conn *websocket.Conn) {
	// Send initial stats
	stats := s.calculateStats()
	conn.WriteJSON(WSMessage{Type: "stats", Data: stats})

	// Send initial spots
	spots := s.FlexRepo.GetAllSpots("0")
	conn.WriteJSON(WSMessage{Type: "spots", Data: spots})

	// Send initial watchlist
	watchlist := s.Watchlist.GetAll()
	conn.WriteJSON(WSMessage{Type: "watchlist", Data: watchlist})

	// Send initial log data
	qsos := s.ContactRepo.GetRecentQSOs("19")
	conn.WriteJSON(WSMessage{Type: "log", Data: qsos})

	logStats := s.ContactRepo.GetQSOStats()
	conn.WriteJSON(WSMessage{Type: "logStats", Data: logStats})

	dxccCount := s.ContactRepo.GetDXCCCount()
	dxccData := map[string]interface{}{
		"worked":     dxccCount,
		"total":      340,
		"percentage": float64(dxccCount) / 340.0 * 100.0,
	}
	conn.WriteJSON(WSMessage{Type: "dxccProgress", Data: dxccData})

	if logBuffer != nil {
		logs := logBuffer.GetAll()
		conn.WriteJSON(WSMessage{Type: "appLogs", Data: logs})
	}
}

func (s *HTTPServer) handleBroadcasts() {
	for msg := range s.broadcast {
		s.wsMutex.RLock()
		for client := range s.wsClients {
			err := client.WriteJSON(msg)
			if err != nil {
				s.Log.Errorf("WebSocket write error: %v", err)
				client.Close()
				s.wsMutex.RUnlock()
				s.wsMutex.Lock()
				delete(s.wsClients, client)
				s.wsMutex.Unlock()
				s.wsMutex.RLock()
			}
		}
		s.wsMutex.RUnlock()
	}
}

func (s *HTTPServer) broadcastUpdates() {
	statsTicker := time.NewTicker(1 * time.Second)
	logTicker := time.NewTicker(5 * time.Second)
	cleanupTicker := time.NewTicker(5 * time.Minute)
	watchlistSaveTicker := time.NewTicker(20 * time.Second)

	defer statsTicker.Stop()
	defer logTicker.Stop()
	defer cleanupTicker.Stop()
	defer watchlistSaveTicker.Stop()

	for {
		select {
		case <-statsTicker.C:
			s.wsMutex.RLock()
			clientCount := len(s.wsClients)
			s.wsMutex.RUnlock()

			if clientCount == 0 {
				continue
			}

			// Broadcast stats
			stats := s.calculateStats()
			s.broadcast <- WSMessage{Type: "stats", Data: stats}

			// Broadcast spots
			spots := s.FlexRepo.GetAllSpots("0")
			s.checkBandOpening(spots)
			s.broadcast <- WSMessage{Type: "spots", Data: spots}

		case <-logTicker.C:
			s.wsMutex.RLock()
			clientCount := len(s.wsClients)
			s.wsMutex.RUnlock()

			if clientCount == 0 {
				continue
			}

			// Broadcast log data every 10 seconds
			qsos := s.ContactRepo.GetRecentQSOs("19")
			s.broadcast <- WSMessage{Type: "log", Data: qsos}

			stats := s.ContactRepo.GetQSOStats()
			s.checkQSOMilestones(stats.Today)
			s.broadcast <- WSMessage{Type: "logStats", Data: stats}

			dxccCount := s.ContactRepo.GetDXCCCount()
			dxccData := map[string]interface{}{
				"worked":     dxccCount,
				"total":      340,
				"percentage": float64(dxccCount) / 340.0 * 100.0,
			}

			s.broadcast <- WSMessage{Type: "dxccProgress", Data: dxccData}

		case <-watchlistSaveTicker.C:
			// Sauvegarder la watchlist périodiquement
			if s.Watchlist != nil {
				s.Watchlist.save()
			}
		}
	}
}

func (s *HTTPServer) getLogs(w http.ResponseWriter, r *http.Request) {
	if logBuffer == nil {
		s.sendJSON(w, APIResponse{Success: true, Data: []LogEntry{}})
		return
	}

	logs := logBuffer.GetAll()
	s.sendJSON(w, APIResponse{Success: true, Data: logs})
}

func (s *HTTPServer) getSpotProcessingStats(w http.ResponseWriter, r *http.Request) {
	received, processed, rejected := GetSpotStats()
	successRate := GetSpotSuccessRate()

	stats := map[string]interface{}{
		"received":    received,
		"processed":   processed,
		"rejected":    rejected,
		"successRate": successRate,
	}

	s.sendJSON(w, APIResponse{Success: true, Data: stats})
}

func (s *HTTPServer) checkQSOMilestones(todayCount int) {
	s.statsMutex.Lock()
	defer s.statsMutex.Unlock()

	if todayCount == s.lastQSOCount {
		return
	}

	milestones := []int{5, 10, 25, 50, 100, 200, 500}

	for _, milestone := range milestones {
		if todayCount >= milestone && s.lastQSOCount < milestone {
			s.broadcast <- WSMessage{
				Type: "milestone",
				Data: map[string]interface{}{
					"type":    "qso",
					"count":   milestone,
					"message": fmt.Sprintf("🎉 %d QSOs today!", milestone),
				},
			}
		}
	}

	s.lastQSOCount = todayCount
}

func (s *HTTPServer) checkBandOpening(spots []FlexSpot) {
	s.statsMutex.Lock()
	defer s.statsMutex.Unlock()

	bandCounts := make(map[string]int)
	for _, spot := range spots {
		bandCounts[spot.Band]++
	}

	// ✅ Seulement surveiller 6M, 10M et 12M
	monitoredBands := []string{"6M", "10M", "12M"}

	now := time.Now()
	for _, band := range monitoredBands {
		count := bandCounts[band]
		if count >= 20 { // Si 20+ spots sur une bande
			lastSeen, exists := s.lastBandOpening[band]
			// Notifier si première fois ou si pas vu depuis 2 heures
			if !exists || now.Sub(lastSeen) > 2*time.Hour {
				s.lastBandOpening[band] = now
				s.broadcast <- WSMessage{
					Type: "milestone",
					Data: map[string]interface{}{
						"type":    "band",
						"band":    band,
						"count":   count,
						"message": fmt.Sprintf("📡 %s opening detected! (%d spots)", band, count),
					},
				}
			}
		}
	}
}

func (s *HTTPServer) getRecentQSOs(w http.ResponseWriter, r *http.Request) {
	limitStr := r.URL.Query().Get("limit")
	if limitStr == "" {
		limitStr = "50"
	}

	qsos := s.ContactRepo.GetRecentQSOs(limitStr)
	s.sendJSON(w, APIResponse{Success: true, Data: qsos})
}

func (s *HTTPServer) getLogStats(w http.ResponseWriter, r *http.Request) {
	stats := s.ContactRepo.GetQSOStats()
	s.sendJSON(w, APIResponse{Success: true, Data: stats})
}

func (s *HTTPServer) getDXCCProgress(w http.ResponseWriter, r *http.Request) {
	workedCount := s.ContactRepo.GetDXCCCount()

	data := map[string]interface{}{
		"worked":     workedCount,
		"total":      340,
		"percentage": float64(workedCount) / 340.0 * 100.0,
	}

	s.sendJSON(w, APIResponse{Success: true, Data: data})
}

func (s *HTTPServer) calculateStats() Stats {
	allSpots := s.FlexRepo.GetAllSpots("0")
	contacts := s.ContactRepo.CountEntries()

	newDXCCCount := 0
	for _, spot := range allSpots {
		if spot.NewDXCC {
			newDXCCCount++
		}
	}

	clusterStatus := "disconnected"
	if s.TCPClient != nil && s.TCPClient.LoggedIn {
		clusterStatus = "connected"
	}

	flexStatus := "disconnected"
	if s.FlexClient != nil && s.FlexClient.IsConnected {
		flexStatus = "connected"
	}

	// Récupérer les stats de traitement des spots
	received, processed, rejected := GetSpotStats()
	successRate := GetSpotSuccessRate()

	return Stats{
		TotalSpots:       len(allSpots),
		NewDXCC:          newDXCCCount,
		ConnectedClients: len(s.TCPServer.Clients),
		TotalContacts:    contacts,
		ClusterStatus:    clusterStatus,
		FlexStatus:       flexStatus,
		MyCallsign:       Cfg.General.Callsign,
		Filters: Filters{
			Skimmer: Cfg.Cluster.Skimmer,
			FT8:     Cfg.Cluster.FT8,
			FT4:     Cfg.Cluster.FT4,
			Beacon:  Cfg.Cluster.Beacon,
		},
		SpotsReceived:   received,
		SpotsProcessed:  processed,
		SpotsRejected:   rejected,
		SpotSuccessRate: successRate,
	}
}

func (s *HTTPServer) corsMiddleware(next 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 == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}

func (s *HTTPServer) getStats(w http.ResponseWriter, r *http.Request) {
	stats := s.calculateStats()
	s.sendJSON(w, APIResponse{Success: true, Data: stats})
}

func (s *HTTPServer) getSpots(w http.ResponseWriter, r *http.Request) {
	limitStr := r.URL.Query().Get("limit")
	if limitStr == "" {
		limitStr = "50"
	}

	spots := s.FlexRepo.GetAllSpots(limitStr)
	s.sendJSON(w, APIResponse{Success: true, Data: spots})
}

func (s *HTTPServer) getSpotByID(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	id := vars["id"]

	spot, err := s.FlexRepo.FindSpotByFlexSpotNumber(id)
	if err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Spot not found"})
		return
	}

	s.sendJSON(w, APIResponse{Success: true, Data: spot})
}

func (s *HTTPServer) getContacts(w http.ResponseWriter, r *http.Request) {
	count := s.ContactRepo.CountEntries()
	data := map[string]interface{}{"totalContacts": count}
	s.sendJSON(w, APIResponse{Success: true, Data: data})
}

type FilterRequest struct {
	Skimmer *bool `json:"skimmer,omitempty"`
	FT8     *bool `json:"ft8,omitempty"`
	FT4     *bool `json:"ft4,omitempty"`
	Beacon  *bool `json:"beacon,omitempty"`
}

func (s *HTTPServer) updateFilters(w http.ResponseWriter, r *http.Request) {
	var req FilterRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"})
		return
	}

	commands := []string{}

	if req.Skimmer != nil {
		if *req.Skimmer {
			commands = append(commands, "set/skimmer")
			Cfg.Cluster.Skimmer = true
		} else {
			commands = append(commands, "set/noskimmer")
			Cfg.Cluster.Skimmer = false
		}
	}

	if req.FT8 != nil {
		if *req.FT8 {
			commands = append(commands, "set/ft8")
			Cfg.Cluster.FT8 = true
		} else {
			commands = append(commands, "set/noft8")
			Cfg.Cluster.FT8 = false
		}
	}

	if req.FT4 != nil {
		if *req.FT4 {
			commands = append(commands, "set/ft4")
			Cfg.Cluster.FT4 = true
		} else {
			commands = append(commands, "set/noft4")
			Cfg.Cluster.FT4 = false
		}
	}

	if req.Beacon != nil {
		if *req.Beacon {
			commands = append(commands, "set/beacon")
			Cfg.Cluster.Beacon = true
		} else {
			commands = append(commands, "set/nobeacon")
			Cfg.Cluster.Beacon = false
		}
	}

	for _, cmd := range commands {
		s.TCPClient.CmdChan <- cmd
	}

	s.sendJSON(w, APIResponse{Success: true, Data: map[string]string{"message": "Filters updated successfully"}})
}

func (s *HTTPServer) getWatchlist(w http.ResponseWriter, r *http.Request) {
	callsigns := s.Watchlist.GetAll()
	s.sendJSON(w, APIResponse{Success: true, Data: callsigns})
}

func (s *HTTPServer) addToWatchlist(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Callsign string `json:"callsign"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"})
		return
	}

	if req.Callsign == "" {
		s.sendJSON(w, APIResponse{Success: false, Error: "Callsign is required"})
		return
	}

	if err := s.Watchlist.Add(req.Callsign); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: err.Error()})
		return
	}

	s.Log.Infof("Added %s to watchlist", req.Callsign)

	// Broadcast updated watchlist to all clients
	s.broadcast <- WSMessage{Type: "watchlist", Data: s.Watchlist.GetAll()}

	s.sendJSON(w, APIResponse{Success: true, Message: "Callsign added to watchlist"})
}

func (s *HTTPServer) removeFromWatchlist(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Callsign string `json:"callsign"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"})
		return
	}

	if req.Callsign == "" {
		s.sendJSON(w, APIResponse{Success: false, Error: "Callsign is required"})
		return
	}

	if err := s.Watchlist.Remove(req.Callsign); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: err.Error()})
		return
	}

	s.Log.Debugf("Removed %s from watchlist", req.Callsign)

	// Broadcast updated watchlist to all clients
	s.broadcast <- WSMessage{Type: "watchlist", Data: s.Watchlist.GetAll()}

	s.sendJSON(w, APIResponse{Success: true, Message: "Callsign removed from watchlist"})
}

func (s *HTTPServer) updateWatchlistNotes(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Callsign string `json:"callsign"`
		Notes    string `json:"notes"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"})
		return
	}

	if req.Callsign == "" {
		s.sendJSON(w, APIResponse{Success: false, Error: "Callsign is required"})
		return
	}

	s.Log.Debugf("Updated notes for %s", req.Callsign)

	// Broadcast updated watchlist to all clients
	s.broadcast <- WSMessage{Type: "watchlist", Data: s.Watchlist.GetAll()}

	s.sendJSON(w, APIResponse{Success: true, Message: "Notes updated"})
}

func (s *HTTPServer) updateWatchlistSound(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Callsign  string `json:"callsign"`
		PlaySound bool   `json:"playSound"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"})
		return
	}

	if req.Callsign == "" {
		s.sendJSON(w, APIResponse{Success: false, Error: "Callsign is required"})
		return
	}

	if err := s.Watchlist.UpdateSound(req.Callsign, req.PlaySound); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: err.Error()})
		return
	}

	s.Log.Debugf("Updated sound setting for %s to %v", req.Callsign, req.PlaySound)

	// Broadcast updated watchlist to all clients
	s.broadcast <- WSMessage{Type: "watchlist", Data: s.Watchlist.GetAll()}

	s.sendJSON(w, APIResponse{Success: true, Message: "Sound setting updated"})
}

func (s *HTTPServer) getWatchlistSpotsWithStatus(w http.ResponseWriter, r *http.Request) {
	// Récupérer tous les spots
	allSpots := s.FlexRepo.GetAllSpots("0")

	// Récupérer la watchlist (maintenant ce sont des WatchlistEntry)
	watchlistEntries := s.Watchlist.GetAll()

	// Extraire juste les callsigns pour la comparaison
	watchlistCallsigns := make([]string, len(watchlistEntries))
	for i, entry := range watchlistEntries {
		watchlistCallsigns[i] = entry.Callsign
	}

	// Filtrer les spots de la watchlist
	var relevantSpots []FlexSpot

	for _, spot := range allSpots {
		isInWatchlist := false

		for _, pattern := range watchlistCallsigns {
			if spot.DX == pattern || strings.HasPrefix(spot.DX, pattern) {
				isInWatchlist = true
				break
			}
		}

		if isInWatchlist {
			relevantSpots = append(relevantSpots, spot)
		}
	}

	type BandModeKey struct {
		Band string
		Mode string
	}

	spotsByBandMode := make(map[BandModeKey][]FlexSpot)

	for _, spot := range relevantSpots {
		key := BandModeKey{Band: spot.Band, Mode: spot.Mode}
		spotsByBandMode[key] = append(spotsByBandMode[key], spot)
	}

	var watchlistSpots []WatchlistSpot

	for key, spots := range spotsByBandMode {
		// Extraire les callsigns uniques
		callsignSet := make(map[string]bool)
		for _, spot := range spots {
			callsignSet[spot.DX] = true
		}

		callsigns := make([]string, 0, len(callsignSet))
		for callsign := range callsignSet {
			callsigns = append(callsigns, callsign)
		}

		workedMap := s.ContactRepo.GetWorkedCallsignsBandMode(callsigns, key.Band, key.Mode)

		// Construire les résultats
		for _, spot := range spots {
			watchlistSpots = append(watchlistSpots, WatchlistSpot{
				DX:              spot.DX,
				FrequencyMhz:    spot.FrequencyMhz,
				Band:            spot.Band,
				Mode:            spot.Mode,
				SpotterCallsign: spot.SpotterCallsign,
				UTCTime:         spot.UTCTime,
				CountryName:     spot.CountryName,
				NewDXCC:         spot.NewDXCC,
				NewBand:         spot.NewBand,
				NewMode:         spot.NewMode,
				Worked:          spot.Worked,
				WorkedBandMode:  workedMap[spot.DX],
			})
		}
	}

	s.sendJSON(w, APIResponse{Success: true, Data: watchlistSpots})
}

func (s *HTTPServer) HandleSolarData(w http.ResponseWriter, r *http.Request) {
	// Récupérer les données depuis hamqsl.com
	resp, err := http.Get("https://www.hamqsl.com/solarxml.php")
	if err != nil {
		s.Log.Errorf("Error fetching solar data: %v", err)
		s.sendJSON(w, APIResponse{Success: false, Error: "Failed to fetch solar data"})
		return
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		s.Log.Errorf("Error reading solar data: %v", err)
		s.sendJSON(w, APIResponse{Success: false, Error: "Failed to read solar data"})
		return
	}

	var solarXML SolarXML
	err = xml.Unmarshal(body, &solarXML)
	if err != nil {
		s.Log.Errorf("Error parsing solar XML: %v", err)
		s.sendJSON(w, APIResponse{Success: false, Error: "Failed to parse solar data"})
		return
	}

	response := map[string]interface{}{
		"sfi":      solarXML.Data.SolarFlux,
		"sunspots": solarXML.Data.Sunspots,
		"aIndex":   solarXML.Data.AIndex,
		"kIndex":   solarXML.Data.KIndex,
		"updated":  solarXML.Data.Updated,
	}

	s.sendJSON(w, APIResponse{Success: true, Data: response})
}

func (s *HTTPServer) handleSendCallsign(w http.ResponseWriter, r *http.Request) {
	var req struct {
		Callsign  string `json:"callsign"`
		Frequency string `json:"frequency"`
		Mode      string `json:"mode"`
	}

	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		s.sendJSON(w, APIResponse{Success: false, Error: "Invalid request"})
		return
	}

	if req.Callsign == "" {
		s.sendJSON(w, APIResponse{Success: false, Error: "Callsign is required"})
		return
	}

	SendUDPMessage([]byte("<CALLSIGN>" + req.Callsign))
	s.Log.Infof("Sent callsign %s to Log4OM via UDP (127.0.0.1:2241)", req.Callsign)

	if Cfg.General.SendFreqModeToLog {
		freqLog4OM := strings.Replace(req.Frequency, ".", "", 1)

		xmlRequestFreq := RemoteControlRequestFreq{
			MessageId:            uuid.New().String(), // Generate a new unique ID
			RemoteControlMessage: "SetTxFrequency",    // Note: Typo matches your required format
			Frequency:            freqLog4OM,
		}

		xmlRequestMode := RemoteControlRequestMode{
			MessageId:            uuid.New().String(), // Generate a new unique ID
			RemoteControlMessage: "SetMode",           // Note: Typo matches your required format
			Mode:                 req.Mode,
		}

		xmlBytesFreq, err := xml.MarshalIndent(xmlRequestFreq, "", "  ")
		if err != nil {
			s.Log.Errorf("Failed to marshal XML: %v", err)
		} else {
			SendUDPMessage([]byte(xmlBytesFreq))
		}

		xmlBytesMode, err := xml.MarshalIndent(xmlRequestMode, "", "  ")
		if err != nil {
			s.Log.Errorf("Failed to marshal XML: %v", err)
		} else {
			SendUDPMessage([]byte(xmlBytesMode))
		}
	}

	if req.Frequency != "" && s.FlexClient != nil && s.FlexClient.IsConnected {
		tuneCmd := fmt.Sprintf("C%v|slice tune 0 %s", CommandNumber, req.Frequency)
		s.FlexClient.Write(tuneCmd)
		CommandNumber++
		time.Sleep(time.Millisecond * 500)
		modeCmd := fmt.Sprintf("C%v|slice s 0 mode=%s", CommandNumber, req.Mode)
		s.FlexClient.Write(modeCmd)
		CommandNumber++
		s.Log.Infof("Sent TUNE command to Flex: %s", tuneCmd)
	}

	s.sendJSON(w, APIResponse{
		Success: true,
		Message: "Callsign sent to Log4OM and radio tuned",
		Data:    map[string]string{"callsign": req.Callsign, "frequency": req.Frequency},
	})
}

func (s *HTTPServer) sendJSON(w http.ResponseWriter, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(data)
}

// ✅ Fonction de shutdown propre
func (s *HTTPServer) shutdownApp(w http.ResponseWriter, r *http.Request) {
	s.Log.Info("Shutdown request received from dashboard")

	s.sendJSON(w, APIResponse{Success: true, Data: map[string]string{"message": "Shutting down FlexDXCluster"}})

	go func() {
		time.Sleep(500 * time.Millisecond)

		// ✅ Utiliser le shutdown centralisé
		GracefulShutdown(s.TCPClient, s.TCPServer, s.FlexClient, s.FlexRepo, s.ContactRepo)

		os.Exit(0)
	}()
}

func (s *HTTPServer) Start() {
	s.Log.Infof("HTTP Server starting on port %s", s.Port)
	s.Log.Infof("Dashboard available at http://localhost:%s", s.Port)

	if err := http.ListenAndServe(":"+s.Port, s.Router); err != nil {
		s.Log.Fatalf("Failed to start HTTP server: %v", err)
	}
}
