//go:build ignore

// IoT 卡分页查询性能基准测试
// 用法:
//   go run ./scripts/perf_query/bench.go
//   go run ./scripts/perf_query/bench.go -base-url http://localhost:3000 -n 20

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"sort"
	"strings"
	"time"
)

var (
	baseURL  = flag.String("base-url", "", "API 服务地址 (默认读取 JUNHONG_SERVER_ADDRESS 环境变量)")
	username = flag.String("username", "perf_test_agent", "登录用户名")
	password = flag.String("password", "PerfTest@123456", "登录密码")
	n        = flag.Int("n", 20, "每个场景执行次数")
	warmup   = flag.Int("warmup", 3, "预热请求次数")
)

type apiResponse struct {
	Code int             `json:"code"`
	Msg  string          `json:"msg"`
	Data json.RawMessage `json:"data"`
}

type loginData struct {
	AccessToken string `json:"access_token"`
}

type listData struct {
	Total    int64 `json:"total"`
	Page     int   `json:"page"`
	PageSize int   `json:"page_size"`
}

type scenario struct {
	Name   string
	Path   string
	Params string
}

func main() {
	flag.Parse()

	if *baseURL == "" {
		addr := os.Getenv("JUNHONG_SERVER_ADDRESS")
		if addr == "" {
			addr = ":3000"
		}
		if strings.HasPrefix(addr, ":") {
			addr = "http://localhost" + addr
		}
		*baseURL = addr
	}

	fmt.Println("╔══════════════════════════════════════════════════════════════╗")
	fmt.Println("║          IoT 卡分页查询性能基准测试                         ║")
	fmt.Println("╚══════════════════════════════════════════════════════════════╝")
	fmt.Printf("  服务地址: %s\n", *baseURL)
	fmt.Printf("  用户: %s\n", *username)
	fmt.Printf("  每场景次数: %d (预热 %d 次)\n", *n, *warmup)
	fmt.Println()

	token := login()
	fmt.Printf("✅ 登录成功 (token: %s...)\n\n", token[:20])

	scenarios := []scenario{
		{
			Name:   "A: 无过滤分页 (第1页)",
			Path:   "/api/admin/iot-cards/standalone",
			Params: "page=1&page_size=20",
		},
		{
			Name:   "B: status+carrier 过滤",
			Path:   "/api/admin/iot-cards/standalone",
			Params: "page=1&page_size=20&status=3&carrier_id=2",
		},
		{
			Name:   "C: 深分页 (第500页)",
			Path:   "/api/admin/iot-cards/standalone",
			Params: "page=500&page_size=20",
		},
		{
			Name:   "D: ICCID 模糊搜索",
			Path:   "/api/admin/iot-cards/standalone",
			Params: "page=1&page_size=20&iccid=12345",
		},
		{
			Name:   "E: 批次号精确过滤",
			Path:   "/api/admin/iot-cards/standalone",
			Params: "page=1&page_size=20&batch_no=PERF-TEST-30M",
		},
		{
			Name:   "F: 运营商+状态+分页",
			Path:   "/api/admin/iot-cards/standalone",
			Params: "page=10&page_size=50&status=1&carrier_id=1",
		},
	}

	results := make([]benchResult, 0, len(scenarios))

	for _, sc := range scenarios {
		r := runScenario(token, sc)
		results = append(results, r)
	}

	printSummary(results)
}

type benchResult struct {
	Name     string
	Total    int64
	Timings  []time.Duration
	Min      time.Duration
	Max      time.Duration
	Avg      time.Duration
	P50      time.Duration
	P95      time.Duration
	P99      time.Duration
	Pass     bool
	ErrorMsg string
}

func runScenario(token string, sc scenario) benchResult {
	fmt.Printf("🔄 %s\n", sc.Name)
	fmt.Printf("   URL: %s?%s\n", sc.Path, sc.Params)

	url := fmt.Sprintf("%s%s?%s", *baseURL, sc.Path, sc.Params)

	result := benchResult{Name: sc.Name}

	for i := 0; i < *warmup; i++ {
		dur, _, err := doRequest(url, token)
		if err != nil {
			result.ErrorMsg = fmt.Sprintf("预热失败: %v", err)
			fmt.Printf("   ❌ %s\n\n", result.ErrorMsg)
			return result
		}
		fmt.Printf("   预热 %d/%d: %v\n", i+1, *warmup, dur.Round(time.Millisecond))
	}

	timings := make([]time.Duration, 0, *n)
	var totalCount int64

	for i := 0; i < *n; i++ {
		dur, data, err := doRequest(url, token)
		if err != nil {
			result.ErrorMsg = fmt.Sprintf("第 %d 次请求失败: %v", i+1, err)
			fmt.Printf("   ❌ %s\n\n", result.ErrorMsg)
			return result
		}
		timings = append(timings, dur)
		if i == 0 {
			totalCount = data.Total
		}
		if (i+1)%5 == 0 || i == 0 {
			fmt.Printf("   [%d/%d] %v\n", i+1, *n, dur.Round(time.Millisecond))
		}
	}

	result.Total = totalCount
	result.Timings = timings
	result.Pass = true
	calcStats(&result)

	passStr := "✅"
	if result.P95 > 1500*time.Millisecond {
		passStr = "❌"
		result.Pass = false
	} else if result.P95 > 1000*time.Millisecond {
		passStr = "⚠️"
	}

	fmt.Printf("   匹配行数: %d\n", result.Total)
	fmt.Printf("   %s P50=%v  P95=%v  P99=%v  (min=%v max=%v)\n\n",
		passStr, result.P50, result.P95, result.P99, result.Min, result.Max)

	return result
}

func doRequest(url, token string) (time.Duration, *listData, error) {
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer "+token)

	start := time.Now()
	resp, err := http.DefaultClient.Do(req)
	dur := time.Since(start)

	if err != nil {
		return dur, nil, fmt.Errorf("HTTP 请求失败: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != 200 {
		return dur, nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body[:min(len(body), 200)]))
	}

	var apiResp apiResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		return dur, nil, fmt.Errorf("JSON 解析失败: %w", err)
	}

	if apiResp.Code != 0 {
		return dur, nil, fmt.Errorf("API 错误 code=%d: %s", apiResp.Code, apiResp.Msg)
	}

	var data listData
	json.Unmarshal(apiResp.Data, &data)

	return dur, &data, nil
}

func login() string {
	url := *baseURL + "/api/auth/login"
	payload := fmt.Sprintf(`{"username":"%s","password":"%s","device":"web"}`, *username, *password)

	resp, err := http.Post(url, "application/json", strings.NewReader(payload))
	if err != nil {
		log.Fatalf("❌ 登录请求失败: %v", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != 200 {
		log.Fatalf("❌ 登录 HTTP %d: %s", resp.StatusCode, string(body[:min(len(body), 500)]))
	}

	var apiResp apiResponse
	if err := json.Unmarshal(body, &apiResp); err != nil {
		log.Fatalf("❌ 登录响应解析失败: %v", err)
	}

	if apiResp.Code != 0 {
		log.Fatalf("❌ 登录失败 code=%d: %s", apiResp.Code, apiResp.Msg)
	}

	var data loginData
	if err := json.Unmarshal(apiResp.Data, &data); err != nil {
		log.Fatalf("❌ 解析 token 失败: %v", err)
	}

	if data.AccessToken == "" {
		log.Fatalf("❌ 登录成功但 token 为空")
	}

	return data.AccessToken
}

func calcStats(r *benchResult) {
	sorted := make([]time.Duration, len(r.Timings))
	copy(sorted, r.Timings)
	sort.Slice(sorted, func(i, j int) bool { return sorted[i] < sorted[j] })

	var sum time.Duration
	for _, d := range sorted {
		sum += d
	}

	n := len(sorted)
	r.Min = sorted[0]
	r.Max = sorted[n-1]
	r.Avg = sum / time.Duration(n)
	r.P50 = percentile(sorted, 50)
	r.P95 = percentile(sorted, 95)
	r.P99 = percentile(sorted, 99)
}

func percentile(sorted []time.Duration, pct int) time.Duration {
	if len(sorted) == 0 {
		return 0
	}
	idx := (pct * len(sorted)) / 100
	if idx >= len(sorted) {
		idx = len(sorted) - 1
	}
	return sorted[idx]
}

func printSummary(results []benchResult) {
	fmt.Println("╔══════════════════════════════════════════════════════════════════════════════╗")
	fmt.Println("║                              测试结果汇总                                   ║")
	fmt.Println("╠══════════════════════════════════════════════════════════════════════════════╣")
	fmt.Printf("║ %-32s │ %8s │ %8s │ %8s │ %8s ║\n", "场景", "匹配行数", "P50", "P95", "结果")
	fmt.Println("╠══════════════════════════════════════════════════════════════════════════════╣")

	allPass := true
	for _, r := range results {
		status := "✅ PASS"
		if r.ErrorMsg != "" {
			status = "❌ ERROR"
			allPass = false
		} else if !r.Pass {
			status = "❌ FAIL"
			allPass = false
		} else if r.P95 > 1000*time.Millisecond {
			status = "⚠️ SLOW"
		}

		p50Str := "-"
		p95Str := "-"
		totalStr := "-"
		if r.ErrorMsg == "" {
			p50Str = r.P50.Round(time.Millisecond).String()
			p95Str = r.P95.Round(time.Millisecond).String()
			totalStr = fmt.Sprintf("%d", r.Total)
		}

		fmt.Printf("║ %-32s │ %8s │ %8s │ %8s │ %8s ║\n",
			r.Name, totalStr, p50Str, p95Str, status)
	}

	fmt.Println("╚══════════════════════════════════════════════════════════════════════════════╝")

	if allPass {
		fmt.Println("\n🎉 所有场景 P95 < 1.5s，满足性能要求!")
	} else {
		fmt.Println("\n⚠️  部分场景未达标，建议:")
		fmt.Println("   1. 运行 go run ./scripts/perf_query/seed.go -action add-index 创建优化索引")
		fmt.Println("   2. 重新运行本测试对比效果")
	}
}

func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}
