package main

import (
	"context"
	"fmt"
	"strings"
	"time"
)

type SpotProcessor struct {
	FlexRepo   *FlexDXClusterRepository
	FlexClient *FlexClient
	HTTPServer *HTTPServer
	SpotChan   chan TelnetSpot
	ctx        context.Context
	cancel     context.CancelFunc
}

func NewSpotProcessor(flexRepo *FlexDXClusterRepository, flexClient *FlexClient, httpServer *HTTPServer, spotChan chan TelnetSpot) *SpotProcessor {
	ctx, cancel := context.WithCancel(context.Background())

	return &SpotProcessor{
		FlexRepo:   flexRepo,
		FlexClient: flexClient,
		HTTPServer: httpServer,
		SpotChan:   spotChan,
		ctx:        ctx,
		cancel:     cancel,
	}
}

func (sp *SpotProcessor) Start() {
	Log.Info("Starting Spot Processor...")

	for {
		select {
		case <-sp.ctx.Done():
			Log.Info("Spot Processor shutting down...")
			return
		case spot := <-sp.SpotChan:
			sp.processSpot(spot)
		}
	}
}

func (sp *SpotProcessor) Stop() {
	Log.Info("Stopping Spot Processor...")
	sp.cancel()
}

func (sp *SpotProcessor) processSpot(spot TelnetSpot) {
	freq := FreqMhztoHz(spot.Frequency)

	flexSpot := FlexSpot{
		CommandNumber:   CommandNumber,
		DX:              spot.DX,
		FrequencyMhz:    freq,
		FrequencyHz:     spot.Frequency,
		Band:            spot.Band,
		Mode:            spot.Mode,
		Source:          "FlexDXCluster",
		SpotterCallsign: spot.Spotter,
		TimeStamp:       time.Now().Unix(),
		UTCTime:         spot.Time,
		LifeTime:        Cfg.Flex.SpotLife,
		OriginalComment: spot.Comment,
		Comment:         spot.Comment,
		Color:           "#ffeaeaea",
		BackgroundColor: "#ff000000",
		Priority:        "5",
		NewDXCC:         spot.NewDXCC,
		NewBand:         spot.NewBand,
		NewMode:         spot.NewMode,
		NewSlot:         spot.NewSlot,
		Worked:          spot.CallsignWorked,
		InWatchlist:     false,
		CountryName:     spot.CountryName,
		DXCC:            spot.DXCC,
	}

	flexSpot.OriginalComment = spot.Comment
	flexSpot.Comment = flexSpot.Comment + " [" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "] [" + flexSpot.CountryName + "]"

	if sp.HTTPServer != nil && sp.HTTPServer.Watchlist != nil {
		if sp.HTTPServer.Watchlist.Matches(flexSpot.DX) {
			flexSpot.InWatchlist = true

			// Mark as seen and update last seen time
			sp.HTTPServer.Watchlist.MarkSeen(flexSpot.DX)

			// Get entry to check if sound should be played
			entry := sp.HTTPServer.Watchlist.GetEntry(flexSpot.DX)
			if entry != nil {
				Log.Infof("🎯 Watchlist match: %s (LastSeen: %s)",
					flexSpot.DX, entry.LastSeenStr)

				// Send notification to websocket clients for sound alert
				if entry.PlaySound && sp.HTTPServer != nil {
					sp.HTTPServer.broadcast <- WSMessage{
						Type: "watchlistAlert",
						Data: map[string]interface{}{
							"callsign":    flexSpot.DX,
							"frequency":   flexSpot.FrequencyMhz,
							"band":        flexSpot.Band,
							"mode":        flexSpot.Mode,
							"countryName": flexSpot.CountryName,
							"playSound":   entry.PlaySound,
						},
					}
				}
			}
		}
	}

	sp.applySpotColors(&flexSpot, spot)
	sp.sendGotifyNotification(flexSpot)

	flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")

	srcFlexSpot, err := sp.FlexRepo.FindDXSameBand(flexSpot)
	if err != nil {
		Log.Debugf("Could not find the DX in the database: %v", err)
	}

	// Vérifier si le spot trouvé est valide (a un ID)
	if srcFlexSpot != nil && srcFlexSpot.ID == 0 {
		srcFlexSpot = nil
	}

	sp.handleSpotStorage(flexSpot, srcFlexSpot)

	if sp.FlexClient != nil && sp.FlexClient.Enabled && sp.FlexClient.IsConnected {
		sp.sendToFlexRadio(flexSpot, srcFlexSpot)
	}
}

func (sp *SpotProcessor) applySpotColors(flexSpot *FlexSpot, spot TelnetSpot) {
	if spot.NewDXCC {
		flexSpot.Priority = "1"
		flexSpot.Comment = flexSpot.Comment + " [New DXCC]"
		if Cfg.General.SpotColorNewEntity != "" {
			flexSpot.Color = Cfg.General.SpotColorNewEntity
			flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewEntity
		} else {
			flexSpot.Color = "#ff3bf908"
			flexSpot.BackgroundColor = "#ff000000"
		}
	} else if spot.DX == Cfg.General.Callsign {
		flexSpot.Priority = "1"
		if Cfg.General.SpotColorMyCallsign != "" {
			flexSpot.Color = Cfg.General.SpotColorMyCallsign
			flexSpot.BackgroundColor = Cfg.General.BackgroundColorMyCallsign
		} else {
			flexSpot.Color = "#ffff0000"
			flexSpot.BackgroundColor = "#ff000000"
		}
	} else if spot.CallsignWorked {
		flexSpot.Priority = "5"
		flexSpot.Comment = flexSpot.Comment + " [Worked]"
		if Cfg.General.SpotColorWorked != "" {
			flexSpot.Color = Cfg.General.SpotColorWorked
			flexSpot.BackgroundColor = Cfg.General.BackgroundColorWorked
		} else {
			flexSpot.Color = "#ff000000"
			flexSpot.BackgroundColor = "#ff00c0c0"
		}
	} else if spot.NewMode && spot.NewBand {
		flexSpot.Priority = "1"
		flexSpot.Comment = flexSpot.Comment + " [New Band & Mode]"
		if Cfg.General.SpotColorNewBandMode != "" {
			flexSpot.Color = Cfg.General.SpotColorNewBandMode
			flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewBandMode
		} else {
			flexSpot.Color = "#ffc603fc"
			flexSpot.BackgroundColor = "#ff000000"
		}
	} else if spot.NewMode && !spot.NewBand {
		flexSpot.Priority = "2"
		flexSpot.Comment = flexSpot.Comment + " [New Mode]"
		if Cfg.General.SpotColorNewMode != "" {
			flexSpot.Color = Cfg.General.SpotColorNewMode
			flexSpot.BackgroundColor = Cfg.General.BackgroundColorNewMode
		} else {
			flexSpot.Color = "#fff9a908"
			flexSpot.BackgroundColor = "#ff000000"
		}
	} else if spot.NewBand && !spot.NewMode {
		flexSpot.Color = "#fff9f508"
		flexSpot.Priority = "3"
		flexSpot.BackgroundColor = "#ff000000"
		flexSpot.Comment = flexSpot.Comment + " [New Band]"
	} else if !spot.NewBand && !spot.NewMode && !spot.NewDXCC && !spot.CallsignWorked && spot.NewSlot {
		flexSpot.Color = "#ff91d2ff"
		flexSpot.Priority = "5"
		flexSpot.BackgroundColor = "#ff000000"
		flexSpot.Comment = flexSpot.Comment + " [New Slot]"
	}
}

func (sp *SpotProcessor) handleSpotStorage(flexSpot FlexSpot, srcFlexSpot *FlexSpot) {
	if srcFlexSpot == nil {
		sp.FlexRepo.CreateSpot(flexSpot)
		CommandNumber++
		if sp.HTTPServer != nil {
			sp.HTTPServer.broadcast <- WSMessage{Type: "spots", Data: sp.FlexRepo.GetAllSpots("0")}
		}
	} else if srcFlexSpot.Band == flexSpot.Band {
		sp.FlexRepo.DeleteSpotByFlexSpotNumber(fmt.Sprintf("%d", srcFlexSpot.FlexSpotNumber))
		sp.FlexRepo.CreateSpot(flexSpot)
		CommandNumber++
	} else {
		sp.FlexRepo.CreateSpot(flexSpot)
		CommandNumber++
	}
}

func (sp *SpotProcessor) sendToFlexRadio(flexSpot FlexSpot, srcFlexSpot *FlexSpot) {
	var stringSpot string

	if srcFlexSpot == nil {
		stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
			flexSpot.CommandNumber, flexSpot.FrequencyMhz, flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
			flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
		CommandNumber++
		sp.FlexClient.SendSpot(stringSpot)
	} else if srcFlexSpot.Band == flexSpot.Band {
		stringSpot = fmt.Sprintf("C%v|spot remove %v", flexSpot.CommandNumber, srcFlexSpot.FlexSpotNumber)
		sp.FlexClient.SendSpot(stringSpot)
		CommandNumber++

		stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
			flexSpot.CommandNumber, flexSpot.FrequencyMhz, flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
			flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
		CommandNumber++
		sp.FlexClient.SendSpot(stringSpot)
	} else {
		stringSpot = fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s",
			flexSpot.CommandNumber, flexSpot.FrequencyMhz, flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign,
			flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
		CommandNumber++
		sp.FlexClient.SendSpot(stringSpot)
	}
}

func (sp *SpotProcessor) sendGotifyNotification(flexSpot FlexSpot) {
	if !Cfg.Gotify.Enable {
		return
	}

	// Cas 1 : Nouveau DXCC - toujours notifier si activé dans la config
	if flexSpot.NewDXCC && Cfg.Gotify.NewDXCC {
		Gotify(flexSpot)
		Log.Debugf("📢 Gotify notification sent: New DXCC - %s", flexSpot.DX)
		return
	}

	// Cas 2 : Callsign dans la watchlist ET non contacté
	if flexSpot.InWatchlist && !flexSpot.Worked && Cfg.Gotify.WatchList {
		Gotify(flexSpot)
		Log.Debugf("📢 Gotify notification sent: Watchlist match (not worked) - %s", flexSpot.DX)
		return
	}

	// Tous les autres cas : pas de notification
	Log.Debugf("🔇 Gotify notification skipped for %s (InWatchlist=%v, Worked=%v, NewDXCC=%v)",
		flexSpot.DX, flexSpot.InWatchlist, flexSpot.Worked, flexSpot.NewDXCC)
}
