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

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

import { RetentionCleanup } from "../../apps/worker/src/retention-cleanup.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
import { registrationNotice } from "../../packages/shared-contracts/src/registration-notice.js";

const baseNow = Date.parse("2026-07-28T11:00:00.000Z");
const roots: string[] = [];
const services: RegistrationService[] = [];

function writeEvidence(value: unknown) {
  const directory = process.env.DADA_EVIDENCE_DIR_RETENTION;
  if (!directory) return;
  mkdirSync(directory, { recursive: true });
  writeFileSync(resolve(directory, "retention-events.json"), `${JSON.stringify(value, null, 2)}\n`);
}

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

describe("TDD-WP1-DEL-002 retention worker", () => {
  it("refuses early/manual deletion and removes each row only at its original expiry", () => {
    const root = mkdtempSync(join(tmpdir(), "dada-wp1-05-retention-"));
    roots.push(root);
    const databasePath = join(root, "dada.sqlite3");
    const service = new RegistrationService({
      challengePepper: Buffer.alloc(32, 0x61), clock: () => baseNow, codeGenerator: () => "120984",
      currentPrivacyNoticeVersion: registrationNotice.version, databasePath,
      invitePepper: Buffer.alloc(32, 0x62), resend: new MockResendAdapter(), sessionPepper: Buffer.alloc(32, 0x63),
    });
    services.push(service);
    const accessExpiry = baseNow + 1_000;
    const anonymousExpiry = baseNow + 2_000;
    service.database.prepare(`
      INSERT INTO private_content_access_logs (log_id, actor_ref, subject_ref, target_ref, content_type, occurred_at, expires_at)
      VALUES (?, ?, ?, ?, 'prompt', ?, ?)
    `).run(randomUUID(), randomUUID(), randomUUID(), randomUUID(), accessExpiry - 180 * 86_400_000, accessExpiry);
    service.database.prepare(`
      INSERT INTO anonymous_retained_events (
        event_id, anonymous_subject_id, event_type, model_id, outcome, error_category,
        credit_delta, occurred_at, expires_at
      ) VALUES (?, ?, 'generation_commit', 'model-a', 'succeeded', NULL, -1, ?, ?)
    `).run(randomUUID(), randomUUID(), baseNow - 10_000, anonymousExpiry);

    expect(() => service.database.prepare("DELETE FROM anonymous_retained_events").run()).toThrow();
    expect(() => service.database.prepare("DELETE FROM private_content_access_logs").run()).toThrow();

    const early = new RetentionCleanup({ clock: () => baseNow, databasePath });
    expect(early.purgeExpired()).toEqual({ admin_operation_logs: 0, anonymous_events: 0, private_access_logs: 0 });
    early.close();
    const firstExpiry = new RetentionCleanup({ clock: () => accessExpiry, databasePath });
    expect(firstExpiry.purgeExpired()).toEqual({ admin_operation_logs: 0, anonymous_events: 0, private_access_logs: 1 });
    firstExpiry.close();
    const secondExpiry = new RetentionCleanup({ clock: () => anonymousExpiry, databasePath });
    expect(secondExpiry.purgeExpired()).toEqual({ admin_operation_logs: 0, anonymous_events: 1, private_access_logs: 0 });
    secondExpiry.close();

    writeEvidence({
      anonymous_deleted_at_expiry: true,
      early_delete_blocked: true,
      private_access_deleted_at_original_expiry: true,
    });
  });
});
