package api

import (
	"encoding/json"
	"flag"
	"os"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"testing"
)

var updateWSFixture = flag.Bool("updateWSFixture", false, "rewrite testdata/ws_types_fixture.json from ws_types.go struct tags")

func TestMain(m *testing.M) {
	flag.Parse()
	os.Exit(m.Run())
}

// Synced with server/web/src/types/ws.ts — see testdata/ws_types_fixture.json.
var wsTypeSamples = map[string]interface{}{
	"WSDashboardInit": WSDashboardInit{},
	"WSAgentOffline":  WSAgentOffline{},
	"WSStatsUpdate":   WSStatsUpdate{},
	"WSCommandResult": WSCommandResult{},
	"WSAgentLog":      WSAgentLog{},
	"WSServerLog":     WSServerLog{},
}

func wsTypeFieldKeys(v interface{}) []string {
	t := reflect.TypeOf(v)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	var keys []string
	for i := 0; i < t.NumField(); i++ {
		tag := t.Field(i).Tag.Get("json")
		if tag == "" || tag == "-" {
			continue
		}
		name, _, _ := strings.Cut(tag, ",")
		if name != "" {
			keys = append(keys, name)
		}
	}
	sort.Strings(keys)
	return keys
}

func wsTypeKeysFromStructs() map[string][]string {
	out := make(map[string][]string, len(wsTypeSamples))
	for name, sample := range wsTypeSamples {
		out[name] = wsTypeFieldKeys(sample)
	}
	return out
}

func TestWSTypeFieldKeysMatchFixture(t *testing.T) {
	got := wsTypeKeysFromStructs()
	fixturePath := filepath.Join("testdata", "ws_types_fixture.json")

	if *updateWSFixture {
		data, err := json.MarshalIndent(got, "", "  ")
		if err != nil {
			t.Fatal(err)
		}
		data = append(data, '\n')
		if err := os.WriteFile(fixturePath, data, 0o644); err != nil {
			t.Fatal(err)
		}
		t.Logf("updated %s", fixturePath)
		return
	}

	raw, err := os.ReadFile(fixturePath)
	if err != nil {
		t.Fatalf("read fixture: %v (run with -updateWSFixture to create)", err)
	}
	var want map[string][]string
	if err := json.Unmarshal(raw, &want); err != nil {
		t.Fatal(err)
	}
	if !reflect.DeepEqual(got, want) {
		t.Fatalf("ws_types fixture drift: re-run go test -run TestWSTypeFieldKeysMatchFixture -updateWSFixture ./internal/api/")
	}
}

func TestWSTypesJSONRoundTrip(t *testing.T) {
	cases := []struct {
		name string
		in   interface{}
	}{
		{"dashboard_init", WSDashboardInit{Agents: []interface{}{}}},
		{"stats", WSStatsUpdate{AgentID: "a1", Hashrate15m: 123.4, CPUUsagePct: 50}},
		{"offline", WSAgentOffline{AgentID: "a1"}},
		{"command", WSCommandResult{AgentID: "a1", Action: "exec", Success: true, Message: "ok"}},
		{"log", WSAgentLog{AgentID: "a1", Content: "tail"}},
		{"server_log", WSServerLog{Line: "started"}},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			data, err := json.Marshal(tc.in)
			if err != nil {
				t.Fatal(err)
			}
			if len(data) == 0 {
				t.Fatal("empty JSON")
			}
		})
	}
}
