package httpapi

import (
	"encoding/json"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/go-chi/chi/v5"
	"github.com/wangjia/pangolin/server/internal/nodes"
)

func testNode() *nodes.NodeRow {
	return &nodes.NodeRow{
		UUID: "n1", Endpoint: "1.2.3.4:443",
		RealityPBK: "pbk", RealityShortID: "sid", RealitySNI: "www.apple.com",
	}
}

func routeOf(t *testing.T, cfg []byte) map[string]any {
	t.Helper()
	var m map[string]any
	if err := json.Unmarshal(cfg, &m); err != nil {
		t.Fatalf("unmarshal config: %v", err)
	}
	return m["route"].(map[string]any)
}

func TestBuildClientConfigSplitCN(t *testing.T) {
	// 开启分流 + base → geoip-cn/geosite-cn rule_set + cn 直连规则;URL 自托管。
	cfg, err := BuildClientConfig(testNode(), "uuid-1", "k",
		ClientConfigOpts{SplitCN: true, RulesBaseURL: "http://node:8080/"})
	if err != nil {
		t.Fatal(err)
	}
	route := routeOf(t, cfg)
	if rs, ok := route["rule_set"].([]any); !ok || len(rs) != 2 {
		t.Fatalf("expected 2 rule_set, got %v", route["rule_set"])
	}
	s := string(cfg)
	for _, want := range []string{
		"geoip-cn", "geosite-cn",
		"http://node:8080/v1/rules/geoip-cn.srs",
		"http://node:8080/v1/rules/geosite-cn.srs",
		"download_detour",
	} {
		if !strings.Contains(s, want) {
			t.Errorf("config missing %q", want)
		}
	}
	foundCN := false
	for _, r := range route["rules"].([]any) {
		rm := r.(map[string]any)
		if rm["outbound"] == "direct" && rm["rule_set"] != nil {
			foundCN = true
		}
	}
	if !foundCN {
		t.Error("missing cn-direct route rule (rule_set→direct)")
	}

	// 关闭分流 → 无 rule_set。
	cfg2, _ := BuildClientConfig(testNode(), "uuid-1", "k", ClientConfigOpts{})
	if _, ok := routeOf(t, cfg2)["rule_set"]; ok {
		t.Error("split off should have no rule_set")
	}

	// 开启但缺 base → 静默不分流(避免渲染出无效 rule_set URL)。
	cfg3, _ := BuildClientConfig(testNode(), "uuid-1", "k", ClientConfigOpts{SplitCN: true})
	if _, ok := routeOf(t, cfg3)["rule_set"]; ok {
		t.Error("split with empty base should have no rule_set")
	}
}

func TestRulesHandler(t *testing.T) {
	dir := t.TempDir()
	if err := os.WriteFile(filepath.Join(dir, "geoip-cn.srs"), []byte("SRS"), 0o644); err != nil {
		t.Fatal(err)
	}
	r := chi.NewRouter()
	r.Get("/v1/rules/{name}", NewRulesHandler(dir).Serve)

	get := func(path string) (int, string) {
		rec := httptest.NewRecorder()
		r.ServeHTTP(rec, httptest.NewRequest("GET", path, nil))
		return rec.Code, rec.Body.String()
	}

	if code, body := get("/v1/rules/geoip-cn.srs"); code != 200 || body != "SRS" {
		t.Fatalf("allowed file: code=%d body=%q, want 200/SRS", code, body)
	}
	if code, _ := get("/v1/rules/passwd"); code != 404 {
		t.Errorf("disallowed name: code=%d, want 404", code)
	}
	if code, _ := get("/v1/rules/geosite-cn.srs"); code != 404 {
		t.Errorf("allowed but missing file: code=%d, want 404", code)
	}
}
