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

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

import { ManagedStorage } from "../../apps/api/src/managed-storage.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
import { StickerReleaseService } from "../../apps/api/src/sticker-releases.js";
import { ProjectPurgeCleanup } from "../../apps/worker/src/project-purge-cleanup.js";

const roots: string[] = [];
const closeables: Array<{ close(): void }> = [];

function fixture() {
  const dataRoot = mkdtempSync(join(tmpdir(), "dada-wp5-06-cleanup-"));
  roots.push(dataRoot);
  mkdirSync(join(dataRoot, "db"), { recursive: true });
  const databasePath = join(dataRoot, "db", "dada.sqlite3");
  const registration = new RegistrationService({
    challengePepper: Buffer.alloc(32, 0x61),
    currentPrivacyNoticeVersion: "p0a-registration-notice-v1",
    databasePath,
    invitePepper: Buffer.alloc(32, 0x62),
    resend: new MockResendAdapter(),
    sessionPepper: Buffer.alloc(32, 0x63),
  });
  const storage = new ManagedStorage({ dataRoot, databasePath });
  const stickers = new StickerReleaseService({ databasePath, storage });
  closeables.push(stickers, storage, registration);
  return { dataRoot, databasePath, database: registration.database, storage };
}

function seedAdmin(database: RegistrationService["database"]) {
  const adminId = randomUUID();
  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(adminId, `${adminId}@example.invalid`, randomUUID(), Date.now());
  database.prepare("INSERT INTO admin_access (user_id, allowed) VALUES (?, 1)").run(adminId);
  return adminId;
}

async function seedHistoricalPair(test: ReturnType<typeof fixture>) {
  const original = await test.storage.commitBufferFixture("sticker_original", "STK2401.png", Buffer.from("original-history"));
  const thumbnail = await test.storage.commitBufferFixture("sticker_thumbnail", "STK2401-thumbnail.png", Buffer.from("thumbnail-history"));
  const insert = test.database.prepare(`
    INSERT INTO sticker_managed_file_history (
      managed_file_id, stable_id, resource_version, file_kind, created_at
    ) VALUES (?, 'STK2401', 'asset-20260701.1', ?, ?)
  `);
  insert.run(original.file_id, "original", Date.now());
  insert.run(thumbnail.file_id, "thumbnail", Date.now());
  return { original, thumbnail };
}

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

describe("TDD-WP5-CLN-001 sticker history cleanup", () => {
  it("denies the whole batch when a reference appears after the candidate snapshot", async () => {
    const test = fixture();
    const adminId = seedAdmin(test.database);
    const files = await seedHistoricalPair(test);
    const candidates = test.storage.listAssetCleanupCandidates();
    expect(candidates.items).toEqual(expect.arrayContaining([
      expect.objectContaining({ file_id: files.original.file_id, file_kind: "original", reference_count: 0, stable_id: "STK2401" }),
      expect.objectContaining({ file_id: files.thumbnail.file_id, file_kind: "thumbnail", reference_count: 0, stable_id: "STK2401" }),
    ]));
    const intent = test.storage.createAssetCleanupIntent({
      actorId: adminId,
      fileIds: [files.original.file_id, files.thumbnail.file_id],
      idempotencyKey: `cleanup-${randomUUID()}-${randomUUID()}`,
      snapshotVersion: candidates.candidate_snapshot_version,
    });

    test.storage.addAssetReference(files.original.file_id, "release");
    expect(() => test.storage.confirmAssetCleanupIntent({
      actorId: adminId,
      confirmationToken: intent.confirmation_token,
      requestId: intent.request_id,
    })).toThrow("ASSET_HISTORY_REFERENCE_CONFLICT");

    expect(test.database.prepare("SELECT status FROM asset_cleanup_requests WHERE request_id = ?").get(intent.request_id)).toEqual({ status: "denied" });
    expect(test.database.prepare("SELECT COUNT(*) AS count FROM file_cleanup_queue WHERE status = 'pending'").get()).toEqual({ count: 0 });
    expect(test.database.prepare("SELECT COUNT(*) AS count FROM admin_operation_logs WHERE target_ref = ? AND result = 'failed'").get(intent.request_id)).toEqual({ count: 1 });
  });

  it("requires the same admin, queues without reducing capacity, then remeasures after physical deletion", async () => {
    const test = fixture();
    const adminId = seedAdmin(test.database);
    const otherAdminId = seedAdmin(test.database);
    const files = await seedHistoricalPair(test);
    const bytesBefore = test.storage.getState().managed_content_bytes;
    const candidates = test.storage.listAssetCleanupCandidates();
    const intent = test.storage.createAssetCleanupIntent({
      actorId: adminId,
      fileIds: [files.original.file_id, files.thumbnail.file_id],
      idempotencyKey: `cleanup-${randomUUID()}-${randomUUID()}`,
      snapshotVersion: candidates.candidate_snapshot_version,
    });

    expect(() => test.storage.confirmAssetCleanupIntent({
      actorId: otherAdminId,
      confirmationToken: intent.confirmation_token,
      requestId: intent.request_id,
    })).toThrow("ASSET_CLEANUP_CANDIDATE_STALE");
    const queued = test.storage.confirmAssetCleanupIntent({
      actorId: adminId,
      confirmationToken: intent.confirmation_token,
      requestId: intent.request_id,
    });
    expect(queued).toMatchObject({ file_count: 2, status: "queued" });
    expect(test.storage.getState().managed_content_bytes).toBe(bytesBefore);
    expect(existsSync(join(test.dataRoot, files.original.relative_path))).toBe(true);
    expect(test.database.prepare("SELECT COUNT(*) AS count FROM file_cleanup_queue WHERE status = 'pending'").get()).toEqual({ count: 2 });

    const worker = new ProjectPurgeCleanup({ dataRoot: test.dataRoot, databasePath: test.databasePath });
    const result = worker.processFileCleanup();
    worker.close();
    expect(result).toEqual({ completed: 2, failed: 0 });
    expect(existsSync(join(test.dataRoot, files.original.relative_path))).toBe(false);
    expect(existsSync(join(test.dataRoot, files.thumbnail.relative_path))).toBe(false);
    expect(test.storage.getState()).toMatchObject({ managed_content_bytes: 0, storage_status: "active" });
    expect(test.database.prepare("SELECT status FROM asset_cleanup_requests WHERE request_id = ?").get(intent.request_id)).toEqual({ status: "completed" });
    expect(test.database.prepare("SELECT operation_type, result FROM admin_operation_logs WHERE target_ref = ? ORDER BY occurred_at").all(intent.request_id)).toEqual([
      { operation_type: "asset_cleanup_requested", result: "succeeded" },
      { operation_type: "asset_cleanup_validated", result: "succeeded" },
      { operation_type: "asset_cleanup_scheduled", result: "succeeded" },
      { operation_type: "asset_cleanup_physical_completed", result: "succeeded" },
    ]);
  });

  it("records a redacted physical failure in the same transaction and leaves the request retryable", async () => {
    const test = fixture();
    const adminId = seedAdmin(test.database);
    const files = await seedHistoricalPair(test);
    const candidates = test.storage.listAssetCleanupCandidates();
    const intent = test.storage.createAssetCleanupIntent({
      actorId: adminId,
      fileIds: [files.original.file_id, files.thumbnail.file_id],
      idempotencyKey: `cleanup-${randomUUID()}-${randomUUID()}`,
      snapshotVersion: candidates.candidate_snapshot_version,
    });
    test.storage.confirmAssetCleanupIntent({
      actorId: adminId,
      confirmationToken: intent.confirmation_token,
      requestId: intent.request_id,
    });
    test.database.prepare(`
      UPDATE file_cleanup_queue SET relative_path = '../outside-fixture'
      WHERE managed_file_id = ?
    `).run(files.original.file_id);

    const worker = new ProjectPurgeCleanup({ dataRoot: test.dataRoot, databasePath: test.databasePath });
    const result = worker.processFileCleanup();
    worker.close();
    expect(result).toEqual({ completed: 1, failed: 1 });
    expect(test.database.prepare("SELECT status FROM asset_cleanup_requests WHERE request_id = ?").get(intent.request_id)).toEqual({ status: "queued" });
    expect(test.database.prepare("SELECT status, last_error FROM file_cleanup_queue WHERE managed_file_id = ?").get(files.original.file_id))
      .toEqual({ last_error: "physical_file_cleanup_failed", status: "failed" });
    const failure = test.database.prepare(`
      SELECT operation_type, result, before_summary, after_summary
      FROM admin_operation_logs WHERE target_ref = ? AND operation_type = 'asset_cleanup_physical_failed'
    `).get(intent.request_id) as { after_summary: string; before_summary: null; operation_type: string; result: string };
    expect(failure).toEqual({
      after_summary: JSON.stringify({ failed_count: 1, status: "retry_pending" }),
      before_summary: null,
      operation_type: "asset_cleanup_physical_failed",
      result: "failed",
    });
    expect(JSON.stringify(failure)).not.toMatch(/outside-fixture|relative_path|absolute_path|image|prompt|secret/i);
  });
});
