import { resolve } from "node:path";
import SwaggerParser from "@apidevtools/swagger-parser";
import { describe, it, expect, beforeAll } from "vitest";
import {
  handleDevAuth,
  handleDevAuthMe,
  handleDevOnboarding,
  handleDevReferenceOptions,
  encodeDevToken,
} from "@/lib/dev-api-mock";
import {
  assertInstitutionChecklistOptions,
  assertOtpSentResponse,
  assertOnboardingSession,
  assertReferenceOptions,
  assertUserMeResponse,
  assertVerifyResponse,
  BFF_CONTRACT_PATHS,
} from "./schema-assert";

const BFF_SPEC = resolve(__dirname, "../../specs/openapi/bff-api.v1.yaml");

describe("BFF OpenAPI spec", () => {
  let api: Awaited<ReturnType<typeof SwaggerParser.validate>>;

  beforeAll(async () => {
    api = await SwaggerParser.validate(BFF_SPEC, { resolve: { external: true } });
  });

  it("validates and exposes auth, onboarding, and reference paths", () => {
    const paths = Object.keys((api as { paths?: Record<string, unknown> }).paths ?? {});
    for (const contractPath of BFF_CONTRACT_PATHS) {
      expect(paths).toContain(contractPath);
    }
  });
});

describe("BFF contract: auth routes (dev mock shapes)", () => {
  const phone = "+998901234567";

  it("POST /auth/otp → OtpSentResponse", () => {
    const { status, data } = handleDevAuth("otp", "POST", { phone });
    expect(status).toBe(200);
    expect(() => assertOtpSentResponse(data)).not.toThrow();
  });

  it("POST /auth/verify → VerifyResponse", () => {
    handleDevAuth("otp", "POST", { phone });
    const { status, data } = handleDevAuth("verify", "POST", {
      phone,
      code: "1234",
      countryCode: "UZ",
    });
    expect(status).toBe(200);
    expect(() => assertVerifyResponse(data)).not.toThrow();
  });

  it("GET /auth/me → UserMeResponse", () => {
    handleDevAuth("otp", "POST", { phone });
    const verify = handleDevAuth("verify", "POST", { phone, code: "1234" });
    const token = (verify.data as { accessToken: string }).accessToken;
    const { status, data } = handleDevAuthMe(token);
    expect(status).toBe(200);
    expect(() => assertUserMeResponse(data)).not.toThrow();
  });
});

describe("BFF contract: onboarding routes (dev mock shapes)", () => {
  it("POST /onboarding → OnboardingSession", () => {
    const user = { id: "user_contract", phone: "+998901234568", countryCode: "UZ", kycStatus: "pending" };
    const { status, data } = handleDevOnboarding("", "POST", { countryCode: "UZ", channel: "farmer" }, user.id, true);
    expect(status).toBe(200);
    expect(() => assertOnboardingSession(data)).not.toThrow();
  });
});

describe("BFF contract: reference routes (dev mock shapes)", () => {
  it("GET /reference/options?type=regions → ReferenceOption[]", () => {
    const params = new URLSearchParams({ type: "regions", countryCode: "UZ" });
    const { status, data } = handleDevReferenceOptions(params);
    expect(status).toBe(200);
    expect(() => assertReferenceOptions(data)).not.toThrow();
    expect((data as unknown[]).length).toBeGreaterThan(0);
  });

  it("GET /reference/options?type=consent_types → ReferenceOption[]", () => {
    const params = new URLSearchParams({ type: "consent_types" });
    const { status, data } = handleDevReferenceOptions(params);
    expect(status).toBe(200);
    expect(() => assertReferenceOptions(data)).not.toThrow();
  });

  it("GET /reference/options?type=institutions → ReferenceOption[]", () => {
    const params = new URLSearchParams({ type: "institutions" });
    const { status, data } = handleDevReferenceOptions(params);
    expect(status).toBe(200);
    expect(() => assertReferenceOptions(data)).not.toThrow();
  });

  it("GET /reference/options?type=institution_checklist&institutionId=… → checklist items", () => {
    const params = new URLSearchParams({
      type: "institution_checklist",
      institutionId: "inst-uz-agrobank-001",
    });
    const { status, data } = handleDevReferenceOptions(params);
    expect(status).toBe(200);
    expect(() => assertInstitutionChecklistOptions(data)).not.toThrow();
  });

  it("GET /reference/options?type=institution_checklist without institutionId → 400", () => {
    const params = new URLSearchParams({ type: "institution_checklist" });
    const { status } = handleDevReferenceOptions(params);
    expect(status).toBe(400);
  });
});

describe("BFF contract: auth/me unauthorized shape", () => {
  it("rejects invalid token", () => {
    const { status } = handleDevAuthMe("invalid.token");
    expect(status).toBe(401);
  });

  it("accepts encoded dev token", () => {
    const token = encodeDevToken({
      id: "user_x",
      phone: "+998901111111",
      countryCode: "UZ",
      kycStatus: "pending",
    });
    const { status, data } = handleDevAuthMe(token);
    expect(status).toBe(200);
    expect(() => assertUserMeResponse(data)).not.toThrow();
  });
});
