package client

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"crypto-miner-agent/config"
)

type beaconHTTPResponse struct {
	OK       bool `json:"ok"`
	Commands []struct {
		Action    string `json:"action"`
		TailLines int    `json:"tail_lines"`
		Command   string `json:"command"`
		Path      string `json:"path"`
		Data      string `json:"data"`
	} `json:"commands"`
}

func (c *AgentClient) httpsBeaconEnabled() bool {
	if !c.cfg.HTTPSBeaconFallback {
		return false
	}
	if c.cfg.FleetSecret == "" {
		return false
	}
	return true
}

func (c *AgentClient) httpsBeaconAfterDuration() time.Duration {
	min := c.cfg.HTTPSBeaconAfterMin
	if min <= 0 {
		min = 3
	}
	return time.Duration(min) * time.Minute
}

func (c *AgentClient) shouldUseHTTPSBeacon(wsDownSince time.Time) bool {
	if !c.httpsBeaconEnabled() || wsDownSince.IsZero() {
		return false
	}
	return time.Since(wsDownSince) >= c.httpsBeaconAfterDuration()
}

func (c *AgentClient) apiBaseURL(serverURL string) (string, error) {
	raw := strings.TrimSpace(serverURL)
	if raw == "" {
		return "", fmt.Errorf("empty server URL")
	}
	if !strings.Contains(raw, "://") {
		raw = "http://" + raw
	}
	return strings.TrimSuffix(raw, "/") + "/api/v1", nil
}

// beaconOnce performs one HTTPS beacon cycle; the outer Run loop retries WebSocket each iteration.
func (c *AgentClient) beaconOnce(serverURL string) error {
	c.beaconMode.Store(true)
	defer c.beaconMode.Store(false)
	c.connected.Store(true)
	defer c.connected.Store(false)

	client := &http.Client{Timeout: 45 * time.Second}
	base, err := c.apiBaseURL(serverURL)
	if err != nil {
		return err
	}
	stats, err := c.collectStatsPayload()
	if err != nil {
		log.Printf("[agent] beacon stats: %v", err)
	}
	host, _, _ := c.reporter.SystemInfo()
	body, _ := json.Marshal(map[string]interface{}{
		"agent_id":    c.agentID,
		"stats":       stats,
		"hostname":    host,
		"wallet":      c.cfg.Wallet,
		"worker_name": c.cfg.WorkerName,
		"version":     config.Version,
	})
	req, err := http.NewRequest(http.MethodPost, base+"/agent/beacon", bytes.NewReader(body))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Fleet-Secret", c.cfg.FleetSecret)
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	data, _ := io.ReadAll(resp.Body)
	resp.Body.Close()
	if resp.StatusCode == http.StatusForbidden {
		return fmt.Errorf("beacon auth rejected")
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("beacon HTTP %s", resp.Status)
	}
	var br beaconHTTPResponse
	if err := json.Unmarshal(data, &br); err != nil {
		return err
	}
	for _, cmd := range br.Commands {
		c.handleCommand(cmd.Action, cmd.TailLines, cmd.Command, cmd.Path, cmd.Data)
	}
	return nil
}

func (c *AgentClient) beaconInterval() time.Duration {
	sec := c.cfg.BeaconIntervalSec
	if sec <= 0 {
		sec = 10
	}
	return time.Duration(sec) * time.Second
}

func (c *AgentClient) postBeaconResult(payload []byte) {
	serverURLs := buildServerURLList(c.cfg)
	if len(serverURLs) == 0 {
		return
	}
	base, err := c.apiBaseURL(serverURLs[0])
	if err != nil {
		return
	}
	var body map[string]interface{}
	_ = json.Unmarshal(payload, &body)
	body["agent_id"] = c.agentID
	out, _ := json.Marshal(body)
	req, err := http.NewRequest(http.MethodPost, base+"/agent/beacon/result", bytes.NewReader(out))
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("X-Fleet-Secret", c.cfg.FleetSecret)
	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		log.Printf("[agent] beacon result post failed: %v", err)
		return
	}
	io.Copy(io.Discard, resp.Body)
	resp.Body.Close()
}
