/**
 * Lightweight schema shape validators for BFF contract tests.
 * Validates dev-mock responses against OpenAPI component shapes without AJV runtime.
 */

export function assertObject(value: unknown, label: string): asserts value is Record<string, unknown> {
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    throw new Error(`${label}: expected object, got ${typeof value}`);
  }
}

export function assertRequiredString(obj: Record<string, unknown>, key: string): void {
  if (typeof obj[key] !== "string" || obj[key] === "") {
    throw new Error(`Missing or invalid required string field: ${key}`);
  }
}

export function assertOtpSentResponse(data: unknown): void {
  assertObject(data, "OtpSentResponse");
  if (typeof data.sent !== "boolean") {
    throw new Error("OtpSentResponse.sent must be boolean");
  }
}

export function assertVerifyResponse(data: unknown): void {
  assertObject(data, "VerifyResponse");
  assertObject(data.user, "VerifyResponse.user");
  assertRequiredString(data.user as Record<string, unknown>, "id");
  assertRequiredString(data.user as Record<string, unknown>, "phone");
  assertRequiredString(data.user as Record<string, unknown>, "countryCode");
  assertRequiredString(data.user as Record<string, unknown>, "kycStatus");
}

export function assertUserMeResponse(data: unknown): void {
  assertObject(data, "UserMeResponse");
  assertRequiredString(data, "id");
  assertRequiredString(data, "phone");
  assertRequiredString(data, "countryCode");
  assertRequiredString(data, "kycStatus");
  if ("activeSession" in data && data.activeSession !== null) {
    assertOnboardingSession(data.activeSession);
  }
}

export function assertOnboardingSession(data: unknown): void {
  assertObject(data, "OnboardingSession");
  assertRequiredString(data, "id");
  assertRequiredString(data, "userId");
  assertRequiredString(data, "countryCode");
  assertRequiredString(data, "status");
  if (typeof data.currentStep !== "number") {
    throw new Error("OnboardingSession.currentStep must be number");
  }
}

export function assertReferenceOptions(data: unknown): void {
  if (!Array.isArray(data)) {
    throw new Error("Reference options must be an array");
  }
  for (const item of data) {
    assertObject(item, "ReferenceOption");
    assertRequiredString(item, "value");
    assertRequiredString(item, "label");
  }
}

export function assertInstitutionChecklistOptions(data: unknown): void {
  assertReferenceOptions(data);
  for (const item of data as Record<string, unknown>[]) {
    const meta = item.metadata;
    if (meta !== undefined) {
      assertObject(meta, "ReferenceOption.metadata");
      if (typeof meta.docType !== "string") {
        throw new Error("institution_checklist item metadata.docType must be string");
      }
      if (typeof meta.required !== "boolean") {
        throw new Error("institution_checklist item metadata.required must be boolean");
      }
    }
  }
}

export const BFF_CONTRACT_PATHS = [
  "/auth/otp",
  "/auth/verify",
  "/auth/me",
  "/auth/logout",
  "/onboarding",
  "/onboarding/{id}",
  "/onboarding/{id}/steps/{step}",
  "/farmer/dashboard",
  "/reference/options",
] as const;

// --- Dashboard v2 (specs/contracts/dashboard-v2.openapi.yaml) ---

export function assertIdentityModuleV2(data: unknown): void {
  assertObject(data, "IdentityModuleV2");
  if (data.farmerId != null && typeof data.farmerId !== "string") {
    throw new Error("identity.farmerId must be string");
  }
  if (data.gis != null) {
    assertObject(data.gis, "identity.gis");
    if (typeof data.gis.areaHa !== "number") throw new Error("identity.gis.areaHa must be number");
    if (typeof data.gis.lat !== "number") throw new Error("identity.gis.lat must be number");
    if (typeof data.gis.lng !== "number") throw new Error("identity.gis.lng must be number");
  }
  if (data.ekyc != null) {
    assertObject(data.ekyc, "identity.ekyc");
  }
  if (data.compliance != null) {
    if (!Array.isArray(data.compliance)) throw new Error("identity.compliance must be array");
    for (const item of data.compliance) {
      assertObject(item, "ComplianceItem");
      assertRequiredString(item, "key");
      assertRequiredString(item, "label");
      if (typeof item.passed !== "boolean") throw new Error("compliance.passed must be boolean");
    }
  }
}

export function assertFinanceModuleV2(data: unknown): void {
  assertObject(data, "FinanceModuleV2");
  if (data.eligibilityScore != null && typeof data.eligibilityScore !== "number") {
    throw new Error("finance.eligibilityScore must be number");
  }
  if (data.sla != null) {
    assertObject(data.sla, "finance.sla");
    if (typeof data.sla.remainingHours !== "number") throw new Error("sla.remainingHours must be number");
    if (typeof data.sla.totalHours !== "number") throw new Error("sla.totalHours must be number");
    assertRequiredString(data.sla, "dueAt");
  }
}

export function assertInputsModuleV2(data: unknown): void {
  assertObject(data, "InputsModuleV2");
  if (data.kpis != null) {
    assertObject(data.kpis, "inputs.kpis");
    for (const key of ["seed", "fertiliser", "equipment", "vouchers", "inTransit"] as const) {
      if (typeof data.kpis[key] !== "number") {
        throw new Error(`inputs.kpis.${key} must be number`);
      }
    }
  }
  if (data.voucher != null) {
    assertObject(data.voucher, "inputs.voucher");
    assertRequiredString(data.voucher, "code");
    if (!Array.isArray(data.voucher.paymentRoute)) {
      throw new Error("voucher.paymentRoute must be array");
    }
  }
}

export function assertFarmerDashboardV2Persona(data: unknown, module: "identity" | "finance" | "inputs"): void {
  assertObject(data, "FarmerDashboardResponseV2");
  assertRequiredString(data, "userId");
  assertRequiredString(data, "phone");
  assertRequiredString(data, "applicationStatus");

  if (module === "identity" && data.identity != null) {
    assertIdentityModuleV2(data.identity);
    const identity = data.identity as Record<string, unknown>;
    assertRequiredString(identity, "farmerId");
    if (identity.gis == null) throw new Error("identity persona requires gis block");
    if (identity.ekyc == null) throw new Error("identity persona requires ekyc block");
  }
  if (module === "finance" && data.finance != null) {
    assertFinanceModuleV2(data.finance);
    const finance = data.finance as Record<string, unknown>;
    if (finance.eligibilityScore == null) throw new Error("finance persona requires eligibilityScore");
    if (finance.sla == null) throw new Error("finance persona requires sla block");
  }
  if (module === "inputs" && data.inputs != null) {
    assertInputsModuleV2(data.inputs);
    const inputs = data.inputs as Record<string, unknown>;
    if (inputs.kpis == null) throw new Error("inputs persona requires kpis block");
    if (inputs.voucher == null) throw new Error("inputs persona requires voucher block");
  }
}
