import { describe, expect, it } from "vitest";
import { PLANS } from "../../lib/usageMeter";
import {
  createLiveRepos,
  mapBillingCheckoutSession,
  mapBillingCheckoutStatus,
  mapBillingSubscription,
  mapByokMeterBlock,
  mapTenantAnalytics,
} from "./repos";

describe("billing API mapping", () => {
  it("maps checkout creation, status, and subscription responses", () => {
    expect(
      mapBillingCheckoutSession({ id: 12, session_id: "cs_1", url: "https://checkout.stripe.com/x", expires_at: "42" }),
    ).toEqual({ id: "12", session_id: "cs_1", url: "https://checkout.stripe.com/x", expires_at: 42 });
    expect(
      mapBillingCheckoutStatus({ id: "local_1", plan_id: "pro", checkout_status: "complete", payment_status: "paid", fulfillment_status: "fulfilled" }),
    ).toEqual({ id: "local_1", plan_id: "pro", checkout_status: "complete", payment_status: "paid", fulfillment_status: "fulfilled" });
    expect(mapBillingCheckoutStatus({ status: "expired" }).checkout_status).toBe("expired");
    expect(
      mapBillingSubscription({ plan_id: "starter", status: "active", current_period_end: "99", cancel_at_period_end: true }),
    ).toEqual({ plan_id: "starter", status: "active", current_period_end: 99, cancel_at_period_end: true });
  });

  it("uses the exact billing endpoints and checkout request fields", async () => {
    const originalFetch = globalThis.fetch;
    const requests: Array<{ url: string; method: string; body: string | null }> = [];
    globalThis.fetch = (async (input: RequestInfo | URL, init?: RequestInit) => {
      requests.push({ url: String(input), method: init?.method ?? "GET", body: (init?.body as string) ?? null });
      const url = String(input);
      const data = url.endsWith("portal-sessions")
        ? { url: "https://billing.stripe.com/x" }
        : url.endsWith("subscription")
          ? { plan_id: "free", status: "none", current_period_end: 0, cancel_at_period_end: false }
          : url.endsWith("checkout-sessions")
            ? { id: "local_1", session_id: "cs_1", url: "https://checkout.stripe.com/x", expires_at: 1 }
            : { id: "local_1", plan_id: "starter", checkout_status: "open", payment_status: "unpaid", fulfillment_status: "pending" };
      return new Response(JSON.stringify({ code: 102000, message: "ok", data, error: null }), {
        status: 200,
        headers: { "Content-Type": "application/json" },
      });
    }) as typeof fetch;
    try {
      const usage = createLiveRepos().usage;
      await usage.createCheckoutSession("starter", "request-1");
      await usage.getCheckoutSession("local/id");
      await usage.getBillingSubscription();
      await usage.createBillingPortalSession();
    } finally {
      globalThis.fetch = originalFetch;
    }

    expect(requests.map((request) => [request.method, request.url])).toEqual([
      ["POST", "/api/v1/billing/checkout-sessions"],
      ["GET", "/api/v1/billing/checkout-sessions/local%2Fid"],
      ["GET", "/api/v1/billing/subscription"],
      ["POST", "/api/v1/billing/portal-sessions"],
    ]);
    expect(JSON.parse(requests[0]?.body ?? "{}")).toEqual({ plan_id: "starter", request_id: "request-1" });
  });
});

describe("member prefs", () => {
  // The admin console reads the plan of the member it is editing. This used to drop the uid and
  // return the admin's own plan, so a Free member could be shown as Pro/unlimited.
  it("sends the requested uid so admins do not see their own plan", async () => {
    const originalFetch = globalThis.fetch;
    const requested: string[] = [];
    globalThis.fetch = (async (input: RequestInfo | URL) => {
      requested.push(String(input));
      return new Response(
        JSON.stringify({ code: 102000, message: "ok", data: { plan_id: "starter", unlimited: false }, error: null }),
        { status: 200, headers: { "Content-Type": "application/json" } },
      );
    }) as typeof fetch;
    try {
      const usage = createLiveRepos().usage;
      await usage.getMemberPrefs("4242");
      await usage.getMemberPrefs();
    } finally {
      globalThis.fetch = originalFetch;
    }

    expect(requested[0]).toBe("/api/v1/usage/prefs?uid=4242");
    expect(requested[1]).toBe("/api/v1/usage/prefs");
  });
});

describe("BYOK usage mapping", () => {
  it("maps BYOK counts separately and fills absent meters with zero", () => {
    const mapped = mapByokMeterBlock(PLANS.free, {
      ai_copy: { credits: 99, count: 3 },
      web_search: { count: 2 },
    });

    expect(mapped.ai_copy).toEqual({ credits: 99, count: 3 });
    expect(mapped.web_search).toEqual({ credits: 0, count: 2 });
    expect(mapped.ai_research).toEqual({ credits: 0, count: 0 });
  });
});

describe("tenant analytics mapping", () => {
  it("keeps platform usage and BYOK counts in separate fields", () => {
    const mapped = mapTenantAnalytics({
      granularity: "day",
      from: "2026-07-01",
      to: "2026-07-02",
      consumed_credits: 4,
      ai_calls: 2,
      by_meter: { ai_copy: { credits: 4, count: 2 } },
      byok: { call_count: 3, by_meter: { web_search: { count: 3 } } },
      series: [{ key: "2026-07-01", consumed_credits: 4, byok_call_count: 3 }],
      members: [{ uid: 7, plan_id: "pro", total_credits: 4, purchased_credits: 10 }],
    });

    expect(mapped.consumed_credits).toBe(4);
    expect(mapped.by_meter.ai_copy).toEqual({ credits: 4, count: 2 });
    expect(mapped.byok.call_count).toBe(3);
    expect(mapped.byok.by_meter.web_search).toEqual({ credits: 0, count: 3 });
    expect(mapped.series[0]?.byok_call_count).toBe(3);
    expect(mapped.members[0]?.uid).toBe("7");
  });

  it("calls the range endpoint with the selected query", async () => {
    const originalFetch = globalThis.fetch;
    let requested = "";
    globalThis.fetch = (async (input: RequestInfo | URL) => {
      requested = String(input);
      return new Response(
        JSON.stringify({
          code: 102000,
          message: "ok",
          data: { granularity: "year", from: "2024-01-01", to: "2026-07-13", series: [], members: [] },
          error: null,
        }),
        { status: 200, headers: { "Content-Type": "application/json" } },
      );
    }) as typeof fetch;
    try {
      await createLiveRepos().usage.getTenantAnalytics({
        granularity: "year",
        from: "2024-01-01",
        to: "2026-07-13",
      });
    } finally {
      globalThis.fetch = originalFetch;
    }

    expect(requested).toContain("/api/v1/usage/tenant-analytics?");
    expect(requested).toContain("granularity=year");
    expect(requested).toContain("from=2024-01-01");
    expect(requested).toContain("to=2026-07-13");
    expect(requested).not.toContain("tenant-summary");
  });
});
