// src/lib/billing/seranking-addon.test.ts — SE-Ranking BYO-key add-on
// subscription sync (slice 2). DB-gated:
// DB_TESTS=1 DATABASE_URL=postgresql://humperdoo@localhost:5432/seo_insight_test \
//   bun test src/lib/billing/seranking-addon.test.ts
import {
  afterAll,
  afterEach,
  beforeAll,
  describe,
  expect,
  mock,
  test,
} from "bun:test";
import type Stripe from "stripe";

mock.module("server-only", () => ({}));
// next/cache's revalidateTag throws outside a real Next request context —
// stub it to a no-op pass-through (same practice used across this project's
// webhook/action test suites, e.g. seat-unblock.test.ts, mcp/*.test.ts).
mock.module("next/cache", () => ({
  revalidatePath: () => {},
  revalidateTag: () => {},
  updateTag: () => {},
  unstable_cache: (fn: unknown) => fn,
}));

const RUN_DB = process.env.DB_TESTS === "1";

const BYOK_LOOKUP_KEY = "addon_seranking_byok";
const ADDON = "seranking_byok";

/** Minimal fake customer.subscription.* event (cast — only the fields the
 *  handler reads: metadata.orgId, items[0].price.lookup_key, status). */
function fakeEvent(over: {
  type?: string;
  orgId?: string | null;
  lookupKey?: string | null;
  status?: string;
  items?: Array<{ lookupKey: string | null }>;
}): Stripe.Event {
  const items =
    over.items !== undefined
      ? over.items.map((i) => ({
          price: i.lookupKey === null ? {} : { lookup_key: i.lookupKey },
        }))
      : over.lookupKey === null
        ? []
        : [{ price: { lookup_key: over.lookupKey ?? BYOK_LOOKUP_KEY } }];
  return {
    type: over.type ?? "customer.subscription.created",
    data: {
      object: {
        id: "sub_test_srk_1",
        status: over.status ?? "active",
        metadata: over.orgId === null ? {} : { orgId: over.orgId },
        items: { data: items },
      },
    },
  } as unknown as Stripe.Event;
}

describe.skipIf(!RUN_DB)("seranking BYOK add-on sync (postgres)", () => {
  let db: typeof import("@/lib/db/client").db;
  let authSchema: typeof import("@/lib/db/auth-schema");
  let schema: typeof import("@/lib/db/domain-schema");

  // Distinct ids from the MCP pack suite, the seranking pack-fulfillment
  // suite, and the parallel seranking client/sync/actions work.
  const ORG = "org-srkbill-addon-1";

  async function cleanup() {
    const { and, eq } = await import("drizzle-orm");
    await db
      .delete(schema.orgAddons)
      .where(
        and(eq(schema.orgAddons.orgId, ORG), eq(schema.orgAddons.addon, ADDON)),
      );
    await db
      .delete(authSchema.organization)
      .where(eq(authSchema.organization.id, ORG));
  }

  async function hasAddon(): Promise<boolean> {
    const { and, eq } = await import("drizzle-orm");
    const rows = await db
      .select({ orgId: schema.orgAddons.orgId })
      .from(schema.orgAddons)
      .where(
        and(eq(schema.orgAddons.orgId, ORG), eq(schema.orgAddons.addon, ADDON)),
      );
    return rows.length > 0;
  }

  beforeAll(async () => {
    ({ db } = await import("@/lib/db/client"));
    authSchema = await import("@/lib/db/auth-schema");
    schema = await import("@/lib/db/domain-schema");
    const { assertTestDb } = await import("@/lib/db/assert-test-db");
    await assertTestDb();
    await cleanup();
    const now = new Date();
    await db.insert(authSchema.organization).values({
      id: ORG,
      name: "Test SE-Ranking Addon Org",
      slug: `org-srkbill-addon-1-${crypto.randomUUID().slice(0, 8)}`,
      createdAt: now,
    });
  });

  afterEach(async () => {
    const { and, eq } = await import("drizzle-orm");
    await db
      .delete(schema.orgAddons)
      .where(
        and(eq(schema.orgAddons.orgId, ORG), eq(schema.orgAddons.addon, ADDON)),
      );
  });

  afterAll(async () => {
    await cleanup();
  });

  test("created with the addon price enables the add-on", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(true);
  });

  test("created twice is idempotent (onConflictDoNothing)", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    const event = fakeEvent({
      type: "customer.subscription.created",
      orgId: ORG,
    });
    await syncSerankingByokAddon(event);
    await syncSerankingByokAddon(event);
    expect(await hasAddon()).toBe(true);
  });

  test("deleted removes the add-on", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(true);

    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.deleted",
        orgId: ORG,
        status: "canceled",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("deleting an already-absent row is a no-op (idempotent)", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.deleted",
        orgId: ORG,
        status: "canceled",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("updated with status canceled removes the add-on", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(true);

    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.updated",
        orgId: ORG,
        status: "canceled",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("updated with status unpaid removes the add-on", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(true);

    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.updated",
        orgId: ORG,
        status: "unpaid",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("updated with status active (re-activation) enables the add-on", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.updated",
        orgId: ORG,
        status: "active",
      }),
    );
    expect(await hasAddon()).toBe(true);
  });

  test("a subscription for another price is ignored", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.created",
        orgId: ORG,
        lookupKey: "solo_monthly",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("a subscription with no items is ignored", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.created",
        orgId: ORG,
        lookupKey: null,
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("a subscription with no orgId metadata is ignored", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: null }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("non-subscription events are ignored", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "checkout.session.completed", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("the BYOK price as the SECOND item of a multi-item subscription is still detected", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.created",
        orgId: ORG,
        items: [{ lookupKey: "solo_monthly" }, { lookupKey: BYOK_LOOKUP_KEY }],
      }),
    );
    expect(await hasAddon()).toBe(true);
  });

  test("updated with status paused removes the add-on (allow-list fail-safe)", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(true);

    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.updated",
        orgId: ORG,
        status: "paused",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("updated with status incomplete removes the add-on (allow-list fail-safe)", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({ type: "customer.subscription.created", orgId: ORG }),
    );
    expect(await hasAddon()).toBe(true);

    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.updated",
        orgId: ORG,
        status: "incomplete",
      }),
    );
    expect(await hasAddon()).toBe(false);
  });

  test("created with status trialing enables the add-on", async () => {
    const { syncSerankingByokAddon } = await import("./seranking-addon");
    await syncSerankingByokAddon(
      fakeEvent({
        type: "customer.subscription.created",
        orgId: ORG,
        status: "trialing",
      }),
    );
    expect(await hasAddon()).toBe(true);
  });
});
