import { createHash, randomUUID } from "node:crypto";
import { existsSync, mkdirSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import { Readable } from "node:stream";

import type BetterSqlite3 from "better-sqlite3";
import { afterEach, describe, expect, it } from "vitest";

import { HARD_LIMIT_BYTES, ManagedStorage, StorageCapacityError, StorageUnavailableError } from "../../apps/api/src/managed-storage.js";
import { StickerReleaseService } from "../../apps/api/src/sticker-releases.js";

const now = Date.parse("2026-08-03T12:00:00.000Z");
const require = createRequire(resolve("apps/api/package.json"));
const Database = require("better-sqlite3") as typeof BetterSqlite3;
const png = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAEklEQVQImWO4E6XxHxkzEBQAANIxHF3ECQOzAAAAAElFTkSuQmCC", "base64");
const webp = Buffer.from("UklGRjoAAABXRUJQVlA4IC4AAADQAQCdASoGAAUAAUAmJaACdLoB+AADsAD+9IiH/pNnibPE2fJI/+Uq8Fjc3wAA", "base64");
const roots: string[] = [];
const closeables: Array<{ close(): void }> = [];

function filesBelow(path: string): string[] {
  if (!existsSync(path)) return [];
  return readdirSync(path, { withFileTypes: true }).flatMap((entry) => {
    const child = join(path, entry.name);
    return entry.isDirectory() ? filesBelow(child) : [child];
  });
}

function evidence(name: string, value: unknown) {
  const directory = process.env.DADA_EVIDENCE_DIR_WP5_UPL;
  if (!directory) return;
  mkdirSync(directory, { recursive: true });
  writeFileSync(resolve(directory, name), `${JSON.stringify(value, null, 2)}\n`);
}

function fixture() {
  const dataRoot = mkdtempSync(join(tmpdir(), "dada-wp5-05-integration-"));
  roots.push(dataRoot);
  mkdirSync(join(dataRoot, "db"), { recursive: true });
  const databasePath = join(dataRoot, "db", "dada.sqlite3");
  const storage = new ManagedStorage({ dataRoot, databasePath });
  const stickers = new StickerReleaseService({ clock: () => now, databasePath, storage });
  closeables.push(stickers, storage);
  return { dataRoot, databasePath, stickers, storage };
}

async function upload(stickers: StickerReleaseService, stableId: string, order: number, bytes = png, mimeType: "image/png" | "image/webp" = "image/png") {
  return stickers.upload({
    actorId: randomUUID(),
    content: Readable.from(bytes),
    enabled: true,
    expectedByteSize: bytes.byteLength,
    expectedMimeType: mimeType,
    expectedSha256: createHash("sha256").update(bytes).digest("hex"),
    fileName: mimeType === "image/png" ? `${stableId}.png` : `${stableId}.webp`,
    idempotencyKey: `upload-${randomUUID()}-${randomUUID()}`,
    order,
    part: 25,
    stableId,
  });
}

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-UPL-001 upload metering", () => {
  it("decodes PNG, publishes an immutable release, meters original and thumbnail, and preserves old release reads", async () => {
    const test = fixture();
    const before = { files: filesBelow(join(test.dataRoot, "managed-assets")), state: test.storage.getState() };
    const published = await upload(test.stickers, "STK1408", 184);
    const afterUpload = test.storage.getState();

    expect(published.release_version).toBe("asset-20260803.1");
    expect(published.item).toMatchObject({ enabled: true, height: 3, mime_type: "image/png", origin: "admin_uploaded", stable_id: "STK1408", width: 4 });
    expect(afterUpload.managed_content_bytes).toBe(published.original.byte_size + published.thumbnail.byte_size);
    expect(test.storage.inspectCounts()).toMatchObject({ active_reservations: 0, managed_files: 2, pending_cleanup: 0 });
    expect(filesBelow(join(test.dataRoot, "managed-assets"))).toHaveLength(2);
    expect(test.stickers.listPublic()).toMatchObject({ count: 1, items: [{ stable_id: "STK1408" }] });

    const disabled = test.stickers.update({ actorId: randomUUID(), enabled: false, stableId: "STK1408" });
    expect(disabled.release_version).toBe("asset-20260803.2");
    expect(test.stickers.listPublic().items).toEqual([]);
    expect(test.stickers.listPublic(published.release_version).items).toHaveLength(1);
    expect(test.stickers.readPublicAsset(published.release_version, "STK1408", "original")?.bytes).toEqual(png);
    expect(test.stickers.readPublicAsset(disabled.release_version, "STK1408", "original")).toBeUndefined();
    const audit = new Database(test.databasePath, { readonly: true });
    expect(audit.prepare(`
      SELECT operation_type, target_ref, result FROM admin_operation_logs
      WHERE operation_type IN ('sticker_release_publish', 'sticker_release_update')
      ORDER BY occurred_at
    `).all()).toEqual([
      { operation_type: "sticker_release_publish", result: "succeeded", target_ref: published.release_version },
      { operation_type: "sticker_release_update", result: "succeeded", target_ref: disabled.release_version },
    ]);
    audit.close();
    const auditFailure = new Database(test.databasePath);
    auditFailure.exec(`
      CREATE TRIGGER force_sticker_release_audit_failure BEFORE INSERT ON admin_operation_logs
      WHEN NEW.operation_type = 'sticker_release_update'
      BEGIN SELECT RAISE(ABORT, 'forced_audit_failure'); END;
    `);
    expect(() => test.stickers.update({ actorId: randomUUID(), enabled: true, stableId: "STK1408" }))
      .toThrow("forced_audit_failure");
    expect(test.stickers.adminView()).toMatchObject({ release_version: disabled.release_version, items: [{ enabled: false }] });
    auditFailure.close();

    evidence("fs-before.json", before);
    evidence("fs-after.json", { files: filesBelow(join(test.dataRoot, "managed-assets")), state: test.storage.getState() });
    evidence("db-diff.json", { releases: test.stickers.inspectCounts(), storage: test.storage.inspectCounts() });
  });

  it("allows exact equality then blocks full and unavailable storage without partial files or rows", async () => {
    const sizing = fixture();
    const measured = await upload(sizing.stickers, "STK1408", 184);
    const writeBytes = measured.original.byte_size + measured.thumbnail.byte_size;

    const exact = fixture();
    exact.storage.applyControlledMeasurement(HARD_LIMIT_BYTES - writeBytes);
    const exactResult = await upload(exact.stickers, "STK1408", 184);
    expect(exactResult.original.byte_size + exactResult.thumbnail.byte_size).toBe(writeBytes);
    expect(exact.storage.getState().storage_status).toBe("full");
    const filesAtFull = filesBelow(join(exact.dataRoot, "managed-assets"));
    const countsAtFull = exact.stickers.inspectCounts();
    await expect(upload(exact.stickers, "STK1409", 185)).rejects.toBeInstanceOf(StorageCapacityError);
    expect(filesBelow(join(exact.dataRoot, "managed-assets"))).toEqual(filesAtFull);
    expect(exact.stickers.inspectCounts()).toEqual(countsAtFull);

    const unavailable = fixture();
    unavailable.storage.setAvailability({ dataRootWritable: false, diskSpaceAvailable: true, sqliteWritable: true });
    await expect(upload(unavailable.stickers, "STK1408", 184)).rejects.toBeInstanceOf(StorageUnavailableError);
    expect(filesBelow(join(unavailable.dataRoot, "managed-assets"))).toEqual([]);
    expect(unavailable.stickers.inspectCounts()).toEqual({ items: 0, releases: 0, upload_receipts: 0 });
  });

  it("rejects a forged PNG before release or managed-file commit", async () => {
    const test = fixture();
    const forged = Buffer.concat([png.subarray(0, 8), Buffer.from("not-a-decodable-png")]);
    await expect(upload(test.stickers, "STK1408", 184, forged)).rejects.toThrow("content_decode_invalid");
    expect(test.storage.inspectCounts()).toMatchObject({ active_reservations: 0, managed_files: 0 });
    expect(test.stickers.inspectCounts()).toEqual({ items: 0, releases: 0, upload_receipts: 0 });
    expect(filesBelow(join(test.dataRoot, "managed-assets"))).toEqual([]);
  });

  it("decodes WebP and rejects stable-ID or part-order conflicts without extra writes", async () => {
    const test = fixture();
    const published = await upload(test.stickers, "STK1408", 184, webp, "image/webp");
    expect(published.item).toMatchObject({ height: 5, mime_type: "image/webp", width: 6 });
    expect(test.stickers.readPublicAsset(published.release_version, "STK1408", "original")?.bytes).toEqual(webp);
    const files = filesBelow(join(test.dataRoot, "managed-assets"));
    const counts = test.stickers.inspectCounts();
    await expect(upload(test.stickers, "STK1408", 185)).rejects.toMatchObject({ httpStatus: 409, reason: "sticker_stable_id_conflict" });
    await expect(upload(test.stickers, "STK1409", 184)).rejects.toMatchObject({ httpStatus: 409, reason: "sticker_order_conflict" });
    expect(filesBelow(join(test.dataRoot, "managed-assets"))).toEqual(files);
    expect(test.stickers.inspectCounts()).toEqual(counts);
  });
});
