import { randomUUID } from "node:crypto";
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

import { afterEach, describe, expect, it } from "vitest";

import { ExternalServiceUsage, ExternalServiceUsageError } from "../../apps/api/src/external-service-usage.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
import { createApp } from "../../apps/api/src/app.js";
import { MockAmapAdapter } from "../../apps/api/src/amap-adapter.js";

const roots: string[] = [];
const registrations: RegistrationService[] = [];

afterEach(() => {
  for (const registration of registrations.splice(0)) registration.close();
  for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

function createRegistration(now = Date.parse("2026-08-04T09:00:00.000Z")) {
  const root = mkdtempSync(join(tmpdir(), "dada-wp6-03-services-"));
  roots.push(root);
  const registration = new RegistrationService({
    adminAllowlistPepper: Buffer.alloc(32, 0x91),
    challengePepper: Buffer.alloc(32, 0x92),
    clock: () => now,
    currentPrivacyNoticeVersion: "p0a-registration-notice-v1",
    databasePath: join(root, "dada.sqlite3"),
    invitePepper: Buffer.alloc(32, 0x93),
    resend: new MockResendAdapter(),
    sessionPepper: Buffer.alloc(32, 0x94),
  });
  registrations.push(registration);
  return registration;
}

function seedAdmin(registration: RegistrationService, now = Date.parse("2026-08-04T09:00:00.000Z")) {
  const userId = randomUUID();
  registration.database.prepare(`
    INSERT INTO users (user_id, normalized_email, role, status, counts_toward_stage_limit, registration_id, created_at)
    VALUES (?, ?, 'super_admin', 'active', 0, ?, ?)
  `).run(userId, `${userId}@example.invalid`, randomUUID(), now);
  registration.database.prepare("INSERT INTO admin_access (user_id, allowed) VALUES (?, 1)").run(userId);
  return userId;
}

function seedUser(registration: RegistrationService, now = Date.parse("2026-08-04T09:00:00.000Z")) {
  const userId = randomUUID();
  registration.database.prepare(`
    INSERT INTO users (user_id, normalized_email, role, status, counts_toward_stage_limit, registration_id, created_at)
    VALUES (?, ?, 'user', 'active', 1, ?, ?)
  `).run(userId, `${userId}@example.invalid`, randomUUID(), now);
  registration.database.prepare("INSERT INTO user_profiles (user_id, creator_name, social_id) VALUES (?, 'Test User', '@test_user')").run(userId);
  registration.database.prepare("INSERT INTO credit_accounts (user_id, available_balance, reserved_balance, updated_at) VALUES (?, 10, 0, ?)").run(userId, now);
  return userId;
}

describe("TDD-WP6-SVC-001 quota hard stop", () => {
  it("claims Resend daily and monthly quotas transactionally and pauses before the next call", () => {
    const registration = createRegistration();
    const usage = registration.serviceUsage;
    usage.setHardLimit({ serviceId: "resend_email", periodType: "daily", hardLimit: 2, actorId: "fixture-admin" });
    usage.setHardLimit({ serviceId: "resend_email", periodType: "monthly", hardLimit: 2, actorId: "fixture-admin" });

    expect(usage.claimResend()).toMatchObject({ allowed: true, remaining: 1 });
    expect(usage.claimResend()).toMatchObject({ allowed: true, remaining: 0 });
    expect(() => usage.claimResend()).toThrowError(ExternalServiceUsageError);
    expect(usage.read("resend_email").every((row) => row.status === "paused_quota")).toBe(true);
  });

  it("does not call a paused provider, and requires a successful health check before recovery", () => {
    const registration = createRegistration();
    const usage = registration.serviceUsage;
    usage.markProviderFailure({ serviceId: "amap_web_service", reason: "provider_unavailable" });
    expect(() => usage.claimAmap()).toThrowError(ExternalServiceUsageError);
    expect(() => usage.recover({ serviceId: "amap_web_service", actorId: randomUUID(), checkId: randomUUID() }))
      .toThrowError(/health_check_required/);

    const check = usage.recordHealthCheck({ serviceId: "amap_web_service", available: true, reason: "ok" });
    expect(usage.recover({ serviceId: "amap_web_service", actorId: randomUUID(), checkId: check.checkId })).toMatchObject({ status: "active" });
  });

  it("keeps a new free period paused until an administrator confirms it", () => {
    const firstNow = Date.parse("2026-08-04T23:59:00.000Z");
    const registration = createRegistration(firstNow);
    registration.serviceUsage.claimAmap(firstNow);
    const nextPeriod = new ExternalServiceUsage({
      database: registration.database,
      clock: () => Date.parse("2026-09-01T00:01:00.000Z"),
    });
    expect(() => nextPeriod.claimAmap()).toThrowError(/service_paused_quota/);
    expect(nextPeriod.read("amap_web_service").find((row) => row.periodStart === Date.parse("2026-08-01T00:00:00.000Z"))?.status).toBe("active");
    expect(nextPeriod.read("amap_web_service").find((row) => row.periodStart === Date.parse("2026-09-01T00:00:00.000Z"))?.status).toBe("paused_quota");
  });

  it("rejects hard-limit increases and records non-sensitive admin audit", () => {
    const registration = createRegistration();
    const usage = registration.serviceUsage;
    expect(() => usage.setHardLimit({ serviceId: "amap_web_service", periodType: "monthly", hardLimit: 1001, actorId: randomUUID() }))
      .toThrowError(/hard_limit_increase_forbidden/);
    expect(registration.database.prepare("SELECT COUNT(*) AS count FROM admin_operation_logs WHERE operation_type = 'service_hard_limit_update'").get())
      .toMatchObject({ count: 1 });
  });

  it("blocks the 81st Resend attempt before the adapter and exposes only current rows to admins", async () => {
    let now = Date.parse("2026-08-04T09:00:00.000Z");
    const registration = createRegistration(now);
    const resend = registration.options.resend as MockResendAdapter;
    registration.serviceUsage.setHardLimit({ serviceId: "resend_email", periodType: "daily", hardLimit: 1, actorId: "fixture-admin" });
    registration.serviceUsage.setHardLimit({ serviceId: "resend_email", periodType: "monthly", hardLimit: 1, actorId: "fixture-admin" });
    const invite = registration.createInvite({ expiresAt: now + 86_400_000, maxUses: 3 });
    await registration.sendRegistrationCode({ email: "quota-one@example.invalid", inviteCode: invite.code });
    expect(() => registration.serviceUsage.claimResend()).toThrowError(/service_paused_quota/);
    expect(resend.calls).toHaveLength(1);

    const adminId = seedAdmin(registration, now);
    const adminSession = registration.issueAuthenticatedSession(adminId, "admin");
    const csrfToken = registration.issueAdminCsrfToken(adminSession.sessionToken);
    const app = await createApp({ browserGate: false, networkBoundary: { allowTestPort: true }, registration });
    await app.ready();
    const response = await app.inject({
      headers: { cookie: `dada_admin_session=${adminSession.sessionToken}`, host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121" },
      method: "GET",
      url: "/api/v1/admin/services",
    });
    expect(response.statusCode).toBe(200);
    expect(response.json().services).toHaveLength(3);
    expect(response.json().services.find((row: { service_id: string; period_type: string }) => row.service_id === "resend_email" && row.period_type === "daily").service_status).toBe("paused_quota");
    const health = await app.inject({
      headers: { "idempotency-key": `${randomUUID()}${randomUUID()}`, "x-csrf-token": csrfToken, cookie: `dada_admin_session=${adminSession.sessionToken}`, host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121" },
      method: "POST",
      payload: { available: true, reason: "new_period" },
      url: "/api/v1/admin/services/resend_email/health-check",
    });
    expect(health.statusCode).toBe(200);
    await app.close();
  });

  it("stops DYN004 after its quota without affecting the rest of the editor", async () => {
    const registration = createRegistration();
    const userId = seedUser(registration);
    const session = registration.issueAuthenticatedSession(userId, "user");
    const csrfToken = registration.issueUserCsrfToken(session.sessionToken);
    const amap = new MockAmapAdapter();
    registration.serviceUsage.setHardLimit({ serviceId: "amap_web_service", periodType: "monthly", hardLimit: 1, actorId: "fixture-admin" });
    const app = await createApp({ amap, browserGate: false, networkBoundary: { allowTestPort: true }, registration });
    const headers = { cookie: `dada_session=${session.sessionToken}`, host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121", "x-csrf-token": csrfToken };
    const first = await app.inject({ headers, method: "POST", payload: { latitude: 30, longitude: 120 }, url: "/api/v1/location/reverse-geocode" });
    const second = await app.inject({ headers, method: "POST", payload: { latitude: 31, longitude: 121 }, url: "/api/v1/location/reverse-geocode" });
    expect(first.statusCode).toBe(200);
    expect(second.statusCode).toBe(503);
    expect(amap.calls).toHaveLength(1);
    await app.close();
  });
});
