/**
 * Contract Glue Generator Tests
 */

import { describe, test, expect } from "bun:test";
import {
  generateContractTypeGlue,
  generateContractTemplate,
  generateContractTypesIndex,
} from "./contract-glue";
import type { RouteSpec } from "../spec/schema";

describe("generateContractTypeGlue", () => {
  test("should generate type glue for route with contract", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users",
      kind: "api",
      module: "generated/routes/api/users.ts",
      contractModule: "spec/contracts/users.contract.ts",
      slotModule: "spec/slots/users.slot.ts",
    };

    const result = generateContractTypeGlue(route);

    expect(result).toContain("// Generated by Mandu");
    expect(result).toContain("import type { InferContract");
    expect(result).toContain("export type UsersContract");
    expect(result).toContain("export type UsersGetQuery");
    expect(result).toContain("export type UsersPostBody");
    expect(result).toContain("export type UsersResponse200");
    expect(result).toContain("export { contract }");
  });

  test("should return empty string for route without contract", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users",
      kind: "api",
      module: "generated/routes/api/users.ts",
      slotModule: "spec/slots/users.slot.ts",
    };

    const result = generateContractTypeGlue(route);

    expect(result).toBe("");
  });

  test("should convert kebab-case to PascalCase", () => {
    const route: RouteSpec = {
      id: "user-profiles",
      pattern: "/api/user-profiles",
      kind: "api",
      module: "generated/routes/api/user-profiles.ts",
      contractModule: "spec/contracts/user-profiles.contract.ts",
    };

    const result = generateContractTypeGlue(route);

    expect(result).toContain("export type UserProfilesContract");
    expect(result).toContain("export type UserProfilesGetQuery");
  });

  test("should convert snake_case to PascalCase", () => {
    const route: RouteSpec = {
      id: "user_settings",
      pattern: "/api/user_settings",
      kind: "api",
      module: "generated/routes/api/user_settings.ts",
      contractModule: "spec/contracts/user_settings.contract.ts",
    };

    const result = generateContractTypeGlue(route);

    expect(result).toContain("export type UserSettingsContract");
  });
});

describe("generateContractTemplate", () => {
  test("should generate template with GET method", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users",
      kind: "api",
      module: "generated/routes/api/users.ts",
      methods: ["GET"],
    };

    const result = generateContractTemplate(route);

    expect(result).toContain("// 📜 Mandu Contract - users");
    expect(result).toContain("// Pattern: /api/users");
    expect(result).toContain('import { z } from "zod"');
    expect(result).toContain("Mandu.contract({");
    expect(result).toContain("GET: {");
    expect(result).toContain("query: z.object({");
    expect(result).not.toContain("POST:");
  });

  test("should generate template with GET and POST methods", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users",
      kind: "api",
      module: "generated/routes/api/users.ts",
      methods: ["GET", "POST"],
    };

    const result = generateContractTemplate(route);

    expect(result).toContain("GET: {");
    expect(result).toContain("POST: {");
    expect(result).toContain("body: z.object({");
    expect(result).toContain("201: z.object({"); // 201 for POST
  });

  test("should generate template with all CRUD methods", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users/:id",
      kind: "api",
      module: "generated/routes/api/users.ts",
      methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
    };

    const result = generateContractTemplate(route);

    expect(result).toContain("GET: {");
    expect(result).toContain("POST: {");
    expect(result).toContain("PUT: {");
    expect(result).toContain("PATCH: {");
    expect(result).toContain("DELETE: {");
  });

  test("should include helpful comments", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users",
      kind: "api",
      module: "generated/routes/api/users.ts",
      methods: ["GET"],
    };

    const result = generateContractTemplate(route);

    expect(result).toContain("// TODO:");
    expect(result).toContain("💡 Contract 사용법:");
    expect(result).toContain("mandu generate");
  });

  test("should set correct description and tags", () => {
    const route: RouteSpec = {
      id: "user-profiles",
      pattern: "/api/user-profiles",
      kind: "api",
      module: "generated/routes/api/user-profiles.ts",
      methods: ["GET"],
    };

    const result = generateContractTemplate(route);

    expect(result).toContain('description: "UserProfiles API"');
    expect(result).toContain('tags: ["user-profiles"]');
  });

  test("should include error response schemas", () => {
    const route: RouteSpec = {
      id: "users",
      pattern: "/api/users",
      kind: "api",
      module: "generated/routes/api/users.ts",
      methods: ["GET"],
    };

    const result = generateContractTemplate(route);

    expect(result).toContain("400: z.object({");
    expect(result).toContain("error: z.string()");
    expect(result).toContain("404: z.object({");
  });
});

describe("generateContractTypesIndex", () => {
  test("should generate index file with all route exports", () => {
    const routeIds = ["users", "posts", "comments"];

    const result = generateContractTypesIndex(routeIds);

    expect(result).toContain("// Generated by Mandu");
    expect(result).toContain('export * from "./users.types";');
    expect(result).toContain('export * from "./posts.types";');
    expect(result).toContain('export * from "./comments.types";');
  });

  test("should handle empty route list", () => {
    const routeIds: string[] = [];

    const result = generateContractTypesIndex(routeIds);

    expect(result).toContain("// Generated by Mandu");
    expect(result).not.toContain("export *");
  });

  test("should handle single route", () => {
    const routeIds = ["users"];

    const result = generateContractTypesIndex(routeIds);

    expect(result).toContain('export * from "./users.types";');
  });
});
