import { resolve } from "node:path";
import SwaggerParser from "@apidevtools/swagger-parser";
import { describe, it, expect, beforeAll } from "vitest";
import { normalizeScore, eligibilityFromRawScore, mapShapToReasonCodes } from "@/lib/scoring-normalize";
import { isAllowedSatelliteUrl } from "@/lib/satellite-image-allowlist";
import { handleDevOnboarding, encodeDevToken, handleDevAuth, handleDevAuthMe } from "@/lib/dev-api-mock";

const ADAPTER_SPEC = resolve(__dirname, "../../specs/openapi/agri-access-adapter.v1.yaml");
const BFF_SPEC = resolve(__dirname, "../../specs/openapi/bff-api.v1.yaml");

describe("agri-access-adapter OpenAPI spec", () => {
  let api: Awaited<ReturnType<typeof SwaggerParser.validate>>;

  beforeAll(async () => {
    api = await SwaggerParser.validate(ADAPTER_SPEC);
  });

  it("validates and exposes worker endpoints", () => {
    const paths = Object.keys((api as { paths?: Record<string, unknown> }).paths ?? {});
    expect(paths).toContain("/api/model-status");
    expect(paths).toContain("/api/analyze");
    expect(paths).toContain("/api/farmer/explain");
  });

  it("defines cbuClass instead of slik_score", () => {
    const schemas = (api as { components?: { schemas?: Record<string, unknown> } }).components?.schemas ?? {};
    const response = schemas.AgriAccessAnalyzeResponse as { properties?: Record<string, unknown> };
    expect(response?.properties?.cbuClass).toBeDefined();
    expect(response?.properties?.slik_score).toBeUndefined();
  });
});

describe("BFF OnboardingStatusResponse enrichment", () => {
  let api: Awaited<ReturnType<typeof SwaggerParser.validate>>;

  beforeAll(async () => {
    api = await SwaggerParser.validate(BFF_SPEC, { resolve: { external: true } });
  });

  it("includes scoringPhase and scoringMeta on status response", () => {
    const schemas = (api as { components?: { schemas?: Record<string, unknown> } }).components?.schemas ?? {};
    const status = schemas.OnboardingStatusResponse as { properties?: Record<string, unknown> };
    expect(status?.properties?.scoringPhase).toBeDefined();
    expect(status?.properties?.scoringMeta).toBeDefined();
    expect(status?.properties?.satelliteImages).toBeDefined();
  });

  it("exposes satellite image proxy route", () => {
    const paths = Object.keys((api as { paths?: Record<string, unknown> }).paths ?? {});
    expect(paths).toContain("/scoring/satellite-image");
  });
});

describe("scoring normalization (AA-007)", () => {
  it("maps 300→0 and 850→1000", () => {
    expect(normalizeScore(300)).toBe(0);
    expect(normalizeScore(850)).toBe(1000);
    expect(normalizeScore(575)).toBe(500);
  });

  it("maps eligibility thresholds", () => {
    expect(eligibilityFromRawScore(700)).toBe("eligible");
    expect(eligibilityFromRawScore(600)).toBe("conditional");
    expect(eligibilityFromRawScore(500)).toBe("not_eligible");
  });

  it("maps SHAP features to RC_* codes", () => {
    const codes = mapShapToReasonCodes([
      { feature: "ndvi", shap_value: 0.42 },
      { feature: "dti_ratio", shap_value: -0.18 },
    ]);
    expect(codes[0]?.code).toBe("RC_NDVI_ANOMALY");
    expect(codes[1]?.code).toBe("RC_DTI_PROXY");
  });
});

describe("satellite image allowlist (SEC-06)", () => {
  it("allows NASA GIBS URLs", () => {
    expect(
      isAllowedSatelliteUrl(
        "https://gibs.earthdata.nasa.gov/wmts/epsg4326/best/NDVI_Terra/default/2024-06-01/250m/5/20/12.png",
      ),
    ).toBe(true);
  });

  it("rejects arbitrary URLs", () => {
    expect(isAllowedSatelliteUrl("https://evil.example.com/image.png")).toBe(false);
  });
});

describe("dev mock scoring phases (AA-022)", () => {
  const phone = "+998909876543";

  function createSession(): { sessionId: string; token: string; userId: string } {
    handleDevAuth("otp", "POST", { phone });
    const verify = handleDevAuth("verify", "POST", { phone, code: "1234", countryCode: "UZ" });
    const token = (verify.data as { accessToken: string }).accessToken;
    const me = handleDevAuthMe(token);
    const userId = (me.data as { id: string }).id;
    const created = handleDevOnboarding("", "POST", { countryCode: "UZ", channel: "farmer" }, userId, true);
    const sessionId = (created.data as { id: string }).id;
    return { sessionId, token, userId };
  }

  it("returns scoringPhase while scoring in progress", () => {
    const { sessionId, userId } = createSession();
    handleDevOnboarding(`${sessionId}/submit`, "POST", null, userId, false);
    const status = handleDevOnboarding(`${sessionId}/status`, "GET", null, userId, false);
    expect(status.status).toBe(200);
    const data = status.data as Record<string, unknown>;
    expect(data.status).toBe("scoring");
    expect(["geo", "documents", "scoring", "finalize"]).toContain(data.scoringPhase);
  });
});
