import { describe, expect, test } from "bun:test";
import {
  ValidationError,
  firstValidationError,
  parseOrThrow,
  validationResponse,
  validationSummary,
  v,
  type InferSchema,
  type ObjectSchema,
} from "../src/index.ts";

describe("validation helper kit", () => {
  test("throws a structured validation error", () => {
    const schema = v.object({ email: v.string().email() });
    expect(() => parseOrThrow(schema, { email: "invalid" })).toThrow(ValidationError);
    const value = parseOrThrow(schema, { email: "user@example.com" });
    const email: string = value.email;
    expect(value).toEqual({ email });
  });

  test("preserves literal unions from oneOf", () => {
    interface ContactInput {
      topic: "general" | "security" | "billing" | "integration";
      priority: 1 | 2 | 3;
    }

    const schema: ObjectSchema<ContactInput> = v.object({
      topic: v.string().oneOf(["general", "security", "billing", "integration"]),
      priority: v.number().oneOf([1, 2, 3]),
    });
    const value = parseOrThrow(schema, { topic: "security", priority: 2 });
    const topic: ContactInput["topic"] = value.topic;
    const priority: ContactInput["priority"] = value.priority;
    expect({ topic, priority }).toEqual({ topic: "security", priority: 2 });

    const _inferredSchema = v.object({
      topic: v.string().oneOf(["general", "security"]),
    });
    type Inferred = InferSchema<typeof _inferredSchema>;
    const inferredTopic: Inferred["topic"] = "general";
    expect(inferredTopic).toBe("general");

    const dynamicOptions: string[] = ["draft", "published"];
    const dynamicSchema = v.object({ status: v.string().oneOf(dynamicOptions) });
    const dynamicValue: string = parseOrThrow(dynamicSchema, {
      status: "draft",
    }).status;
    expect(dynamicValue).toBe("draft");
  });

  test("normalizes error summaries and HTTP responses", async () => {
    const errors = { email: "Email is invalid", password: "Password is required" };
    expect(firstValidationError(errors)).toBe("Email is invalid");
    expect(validationSummary(errors)).toHaveLength(2);
    expect((await validationResponse({ ok: false, value: {}, errors }).json()).ok).toBe(false);
  });
});
