// 工具函数单测
// 依据：02-architecture-design.md §13 测试策略、project_rules §3.4 TypeScript 规则

import { describe, it, expect, vi, beforeEach } from "vitest";
import {
  cn,
  formatDate,
  formatDateTime,
  formatRelativeTime,
  formatNumber,
  formatPercent,
  generateId,
  scoreToGrade,
  gradeToChinese,
  getLocalStorage,
  setLocalStorage,
  removeLocalStorage,
  debounce,
  throttle,
  getBackoffDelay,
  generateTraceId,
} from "./utils";

describe("cn", () => {
  it("合并多个类名", () => {
    expect(cn("foo", "bar")).toBe("foo bar");
  });

  it("支持条件类名", () => {
    const isHidden = false;
    const isVisible = true;
    expect(cn("base", isHidden && "hidden", isVisible && "visible")).toBe(
      "base visible",
    );
  });

  it("合并 Tailwind 冲突类（后者优先）", () => {
    expect(cn("px-2", "px-4")).toBe("px-4");
  });
});

describe("formatDate", () => {
  it("格式化 ISO 字符串（zh-CN）", () => {
    const result = formatDate("2026-07-10T00:00:00Z", "zh-CN");
    expect(result).toContain("2026");
    expect(result).toContain("7");
    expect(result).toContain("10");
  });

  it("格式化 Date 对象", () => {
    const d = new Date("2026-07-10T00:00:00Z");
    const result = formatDate(d, "en-US");
    expect(result).toContain("2026");
  });

  it("默认 locale 为 zh-CN", () => {
    const result = formatDate("2026-07-10T00:00:00Z");
    expect(result).toContain("2026");
  });
});

describe("formatDateTime", () => {
  it("包含日期和时间", () => {
    const result = formatDateTime("2026-07-10T09:30:00Z", "zh-CN");
    expect(result).toContain("2026");
  });
});

describe("formatRelativeTime", () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-07-10T12:00:00Z"));
  });

  it("秒级（刚刚）", () => {
    const result = formatRelativeTime("2026-07-10T11:59:55Z", "zh-CN");
    expect(result).toMatch(/秒|second/);
  });

  it("分钟级", () => {
    const result = formatRelativeTime("2026-07-10T11:30:00Z", "zh-CN");
    expect(result).toMatch(/分|minute/);
  });

  it("小时级", () => {
    const result = formatRelativeTime("2026-07-10T09:00:00Z", "zh-CN");
    expect(result).toMatch(/小时|hour/);
  });

  it("天级", () => {
    const result = formatRelativeTime("2026-07-08T09:00:00Z", "zh-CN");
    expect(result).toMatch(/天|day/);
  });
});

describe("formatNumber", () => {
  it("格式化整数（zh-CN 千分位）", () => {
    expect(formatNumber(1234567, "zh-CN")).toBe("1,234,567");
  });

  it("格式化小数", () => {
    const result = formatNumber(1234.56, "en-US");
    expect(result).toBe("1,234.56");
  });
});

describe("formatPercent", () => {
  it("格式化百分比（0-1 → 0-100%）", () => {
    const result = formatPercent(0.96, "zh-CN");
    expect(result).toContain("96");
    expect(result).toContain("%");
  });

  it("保留指定位数", () => {
    const result = formatPercent(0.9633, "en-US", 2);
    expect(result).toBe("96.33%");
  });
});

describe("generateId", () => {
  it("返回字符串", () => {
    expect(typeof generateId()).toBe("string");
  });

  it("每次调用返回不同值", () => {
    expect(generateId()).not.toBe(generateId());
  });
});

describe("scoreToGrade", () => {
  it("A 等级（>=90%）", () => {
    expect(scoreToGrade(95, 100)).toBe("A");
    expect(scoreToGrade(90, 100)).toBe("A");
  });

  it("B 等级（80-89%）", () => {
    expect(scoreToGrade(89, 100)).toBe("B");
    expect(scoreToGrade(80, 100)).toBe("B");
  });

  it("C 等级（70-79%）", () => {
    expect(scoreToGrade(75, 100)).toBe("C");
    expect(scoreToGrade(70, 100)).toBe("C");
  });

  it("D 等级（60-69%）", () => {
    expect(scoreToGrade(65, 100)).toBe("D");
    expect(scoreToGrade(60, 100)).toBe("D");
  });

  it("F 等级（<60%）", () => {
    expect(scoreToGrade(59, 100)).toBe("F");
    expect(scoreToGrade(0, 100)).toBe("F");
  });

  it("支持非 100 满分", () => {
    expect(scoreToGrade(45, 50)).toBe("A"); // 90%
    expect(scoreToGrade(29, 50)).toBe("F"); // 58%
  });
});

describe("gradeToChinese", () => {
  it("A → 优秀", () => {
    expect(gradeToChinese("A")).toBe("优秀");
  });
  it("B → 良好", () => {
    expect(gradeToChinese("B")).toBe("良好");
  });
  it("C → 及格", () => {
    expect(gradeToChinese("C")).toBe("及格");
  });
  it("D → 及格", () => {
    expect(gradeToChinese("D")).toBe("及格");
  });
  it("F → 不及格", () => {
    expect(gradeToChinese("F")).toBe("不及格");
  });
  it("未知等级原样返回", () => {
    expect(gradeToChinese("X")).toBe("X");
  });
});

describe("localStorage 工具（SSR 安全）", () => {
  it("setLocalStorage / getLocalStorage", () => {
    expect(setLocalStorage("foo", "bar")).toBe(true);
    expect(getLocalStorage("foo")).toBe("bar");
  });

  it("removeLocalStorage", () => {
    setLocalStorage("foo", "bar");
    removeLocalStorage("foo");
    expect(getLocalStorage("foo")).toBeNull();
  });

  it("未设置的 key 返回 null", () => {
    expect(getLocalStorage("not-exist")).toBeNull();
  });
});

describe("debounce", () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });

  it("延迟执行", () => {
    const fn = vi.fn();
    const debounced = debounce(fn, 100);
    debounced();
    expect(fn).not.toHaveBeenCalled();
    vi.advanceTimersByTime(100);
    expect(fn).toHaveBeenCalledTimes(1);
  });

  it("多次调用只执行最后一次", () => {
    const fn = vi.fn();
    const debounced = debounce(fn, 100);
    debounced("a");
    debounced("b");
    debounced("c");
    vi.advanceTimersByTime(100);
    expect(fn).toHaveBeenCalledTimes(1);
    expect(fn).toHaveBeenCalledWith("c");
  });
});

describe("throttle", () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2026-07-10T00:00:00Z"));
  });

  it("首次立即执行", () => {
    const fn = vi.fn();
    const throttled = throttle(fn, 100);
    throttled();
    expect(fn).toHaveBeenCalledTimes(1);
  });

  it("节流窗口内只执行一次", () => {
    const fn = vi.fn();
    const throttled = throttle(fn, 100);
    throttled();
    throttled();
    throttled();
    expect(fn).toHaveBeenCalledTimes(1);
  });
});

describe("getBackoffDelay", () => {
  it("指数退避", () => {
    expect(getBackoffDelay(0, 1000)).toBe(1000);
    expect(getBackoffDelay(1, 1000)).toBe(2000);
    expect(getBackoffDelay(2, 1000)).toBe(4000);
    expect(getBackoffDelay(3, 1000)).toBe(8000);
  });

  it("上限 30 秒", () => {
    expect(getBackoffDelay(10, 1000)).toBe(30000);
  });

  it("默认 baseDelay 1000", () => {
    expect(getBackoffDelay(2)).toBe(4000);
  });
});

describe("generateTraceId", () => {
  it("返回 parent- 前缀的字符串", () => {
    const id = generateTraceId();
    expect(id).toMatch(/^parent-/);
  });

  it("每次调用返回不同值", () => {
    expect(generateTraceId()).not.toBe(generateTraceId());
  });
});
