import { describe, it, expect } from "vitest";
import { ApiError } from "@/lib/api";
import {
  validateUsername,
  validatePassword,
  validateEmail,
  validateIdeaContent,
  validateRoomName,
  requireString,
  validatePanicRoomId,
} from "@/lib/validation";

describe("validateUsername", () => {
  it("accepts 2-16 character names", () => {
    expect(validateUsername("ab")).toBe("ab");
    expect(validateUsername("a".repeat(16))).toBe("a".repeat(16));
    expect(validateUsername("用户名")).toBe("用户名");
  });

  it("trims whitespace", () => {
    expect(validateUsername("  hello  ")).toBe("hello");
  });

  it("rejects names shorter than 2 chars", () => {
    expect(() => validateUsername("a")).toThrow(ApiError);
    expect(() => validateUsername("")).toThrow(ApiError);
    expect(() => validateUsername("   ")).toThrow(ApiError);
  });

  it("rejects names longer than 16 chars", () => {
    expect(() => validateUsername("a".repeat(17))).toThrow(ApiError);
  });
});

describe("validatePassword", () => {
  it("accepts 6-128 character passwords", () => {
    expect(() => validatePassword("123456")).not.toThrow();
    expect(() => validatePassword("a".repeat(128))).not.toThrow();
  });

  it("rejects passwords shorter than 6 chars", () => {
    expect(() => validatePassword("12345")).toThrow(ApiError);
    expect(() => validatePassword("")).toThrow(ApiError);
  });

  it("rejects passwords longer than 128 chars", () => {
    expect(() => validatePassword("a".repeat(129))).toThrow(ApiError);
  });

  it("uses custom label in error message", () => {
    expect(() => validatePassword("12345", "新密码")).toThrow("新密码至少 6 个字符");
  });
});

describe("validateEmail", () => {
  it("accepts valid emails", () => {
    expect(() => validateEmail("test@example.com")).not.toThrow();
    expect(() => validateEmail("user.name@domain.co")).not.toThrow();
  });

  it("rejects invalid emails", () => {
    expect(() => validateEmail("notanemail")).toThrow(ApiError);
    expect(() => validateEmail("@domain.com")).toThrow(ApiError);
    expect(() => validateEmail("user@")).toThrow(ApiError);
    expect(() => validateEmail("user @domain.com")).toThrow(ApiError);
  });
});

describe("validateIdeaContent", () => {
  it("accepts valid content and trims", () => {
    expect(validateIdeaContent("  去公园  ")).toBe("去公园");
    expect(validateIdeaContent("a")).toBe("a");
  });

  it("rejects empty/falsy content", () => {
    expect(() => validateIdeaContent("")).toThrow(ApiError);
    expect(() => validateIdeaContent(null)).toThrow(ApiError);
    expect(() => validateIdeaContent(undefined)).toThrow(ApiError);
    expect(() => validateIdeaContent("   ")).toThrow(ApiError);
  });

  it("rejects non-string content", () => {
    expect(() => validateIdeaContent(123)).toThrow(ApiError);
  });

  it("rejects content over 200 chars", () => {
    expect(() => validateIdeaContent("a".repeat(201))).toThrow(ApiError);
    expect(validateIdeaContent("a".repeat(200))).toBe("a".repeat(200));
  });
});

describe("validateRoomName", () => {
  it("accepts valid room name", () => {
    expect(validateRoomName("我的房间")).toBe("我的房间");
  });

  it("uses fallback for empty name", () => {
    expect(validateRoomName("")).toBe("我们的周末");
    expect(validateRoomName(null)).toBe("我们的周末");
    expect(validateRoomName(undefined)).toBe("我们的周末");
  });

  it("uses custom fallback", () => {
    expect(validateRoomName("", "默认名")).toBe("默认名");
  });

  it("rejects names over 30 chars", () => {
    expect(() => validateRoomName("a".repeat(31))).toThrow(ApiError);
    expect(validateRoomName("a".repeat(30))).toBe("a".repeat(30));
  });
});

describe("requireString", () => {
  it("returns string value when valid", () => {
    expect(requireString("hello", "field")).toBe("hello");
  });

  it("throws for empty/falsy values", () => {
    expect(() => requireString("", "字段")).toThrow("字段不能为空");
    expect(() => requireString(null, "字段")).toThrow(ApiError);
    expect(() => requireString(undefined, "字段")).toThrow(ApiError);
    expect(() => requireString("   ", "字段")).toThrow(ApiError);
  });

  it("throws for non-string values", () => {
    expect(() => requireString(123, "字段")).toThrow(ApiError);
  });
});

describe("validatePanicRoomId", () => {
  it("accepts 6-char alphanumeric room id and normalizes uppercase", () => {
    expect(validatePanicRoomId("ab12cd")).toBe("AB12CD");
    expect(validatePanicRoomId("ROOM01")).toBe("ROOM01");
  });

  it("rejects invalid room id format", () => {
    expect(() => validatePanicRoomId("1234")).toThrow(ApiError);
    expect(() => validatePanicRoomId("1234567")).toThrow(ApiError);
    expect(() => validatePanicRoomId("12-45a")).toThrow(ApiError);
  });
});
