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

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

import { createApp } from "../../apps/api/src/app.js";
import { RegistrationService } from "../../apps/api/src/registration.js";
import { MockResendAdapter } from "../../apps/api/src/resend-adapter.js";
import { createAssetReleaseManifest } from "../../packages/asset-release-manifest/src/index.js";

const now = Date.parse("2026-08-03T10:00:00.000Z");
const releaseVersion = "asset-20260803.1";
const publicId = "7f0c9530-a7d9-4bf1-8c65-0e9298dd04ac";
const previewId = "ab18fd72-60e1-44e3-a9a0-3dfccb12e17c";
const ungrantedPreviewId = "d8fe890d-6df4-46a9-a578-96c1f8361ac0";
const privateId = "e3792605-5252-4d3b-a101-827408ab3515";
const headers = { host: "127.0.0.1:43121", origin: "http://127.0.0.1:43121" };
const roots: string[] = [];
const registrations: RegistrationService[] = [];

function addUser(registration: RegistrationService, role: "super_admin" | "user") {
  const userId = randomUUID();
  registration.database.prepare(`INSERT INTO users (
    user_id, normalized_email, role, status, counts_toward_stage_limit, registration_id, created_at
  ) VALUES (?, ?, ?, 'active', ?, ?, ?)`).run(
    userId,
    `${role}-${userId}@example.invalid`,
    role,
    role === "user" ? 1 : 0,
    randomUUID(),
    now,
  );
  if (role === "user") {
    registration.database.prepare("INSERT INTO user_profiles (user_id, creator_name, social_id) VALUES (?, 'Asset User', '@asset_user')").run(userId);
    registration.database.prepare("INSERT INTO credit_accounts (user_id, available_balance, reserved_balance, updated_at) VALUES (?, 10, 0, ?)").run(userId, now);
  } else {
    registration.database.prepare("INSERT INTO admin_access (user_id, allowed) VALUES (?, 1)").run(userId);
  }
  return { session: registration.issueAuthenticatedSession(userId, role === "user" ? "user" : "admin"), userId };
}

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

function harness() {
  const root = mkdtempSync(join(tmpdir(), "dada-wp5-04-api-"));
  roots.push(root);
  const registration = new RegistrationService({
    challengePepper: Buffer.alloc(32, 0x31), clock: () => now,
    currentPrivacyNoticeVersion: "p0a-registration-notice-v1", databasePath: join(root, "dada.sqlite3"),
    invitePepper: Buffer.alloc(32, 0x32), resend: new MockResendAdapter(), sessionPepper: Buffer.alloc(32, 0x33),
  });
  registrations.push(registration);
  const owner = addUser(registration, "user");
  const intruder = addUser(registration, "user");
  const admin = addUser(registration, "super_admin");
  const assetReleases = createAssetReleaseManifest({
    items: [
      { access_class: "public_release_asset", cache_kind: "thumbnail", content: Buffer.from("public"), mime_type: "image/png", relative_path: "public/FLOWER001.png", resource_id: publicId, root_ref: "canonical-assets" },
      { access_class: "internal_preview_asset", content: Buffer.from("preview"), mime_type: "image/webp", relative_path: "preview/FLOWER009.webp", resource_id: previewId, root_ref: "canonical-assets" },
      { access_class: "internal_preview_asset", content: Buffer.from("ungranted-preview"), mime_type: "image/webp", relative_path: "preview/FLOWER010.webp", resource_id: ungrantedPreviewId, root_ref: "canonical-assets" },
      { access_class: "private_user_asset", content: Buffer.from("private"), mime_type: "image/png", owner_id: owner.userId, relative_path: "private/generated.png", resource_id: privateId, root_ref: "managed-assets" },
    ],
    release_version: releaseVersion,
  });
  let previewGranted = true;
  let previewChecks = 0;
  const appPromise = createApp({
    assetReleases,
    browserGate: false,
    networkBoundary: { allowTestPort: true },
    previewAssetAuthorizer: ({ resourceId, userId }) => {
      previewChecks += 1;
      return previewGranted && userId === owner.userId && resourceId === previewId;
    },
    privateAssetAdminAuthorizer: ({ adminUserId, ownerId }) => adminUserId === admin.userId && ownerId === owner.userId,
    registration,
  });
  return { admin, appPromise, intruder, owner, previewChecks: () => previewChecks, revokePreview: () => { previewGranted = false; } };
}

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

describe("TDD-WP5-RES-001 three access classes", () => {
  it("separates route projections, per-request authorization, and cache headers", async () => {
    const test = harness();
    const app = await test.appPromise;
    const ownerCookie = `dada_session=${test.owner.session.sessionToken}`;
    const intruderCookie = `dada_session=${test.intruder.session.sessionToken}`;
    const adminCookie = `dada_admin_session=${test.admin.session.sessionToken}`;

    const publicManifest = await app.inject({ headers, method: "GET", url: `/api/v1/assets/public/${releaseVersion}/manifest` });
    const publicAsset = await app.inject({ headers, method: "GET", url: `/api/v1/assets/public/${releaseVersion}/${publicId}` });
    const previewManifest = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/assets/preview/${releaseVersion}/manifest` });
    const previewAsset = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/assets/preview/${releaseVersion}/${previewId}` });
    const privateManifest = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/manifest` });
    const privateAsset = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/${privateId}` });
    const deniedPrivate = await app.inject({ headers: { ...headers, cookie: intruderCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/${privateId}` });
    const controlledAdmin = await app.inject({ headers: { ...headers, cookie: adminCookie }, method: "GET", url: `/api/v1/private-assets/${releaseVersion}/${privateId}` });

    expect(publicManifest.statusCode).toBe(200);
    expect(publicManifest.headers["cache-control"]).toBe("public, max-age=31536000, immutable");
    expect(publicManifest.json().items).toEqual([expect.objectContaining({ access_class: "public_release_asset", resource_id: publicId })]);
    expect(JSON.stringify(publicManifest.json())).not.toMatch(/preview|private|relative_path|root_ref|[A-Z]:\\\\/i);
    expect(publicAsset.statusCode).toBe(200);
    expect(publicAsset.rawPayload).toEqual(Buffer.from("public"));
    expect(publicAsset.headers["cache-control"]).toBe("public, max-age=31536000, immutable");

    expect(previewManifest.statusCode).toBe(200);
    expect(previewManifest.headers["cache-control"]).toBe("private, no-store");
    expect(previewManifest.json().items).toEqual([expect.objectContaining({ resource_id: previewId })]);
    expect(JSON.stringify(previewManifest.json())).not.toContain(ungrantedPreviewId);
    expect(previewAsset.statusCode).toBe(200);
    expect(previewAsset.headers["cache-control"]).toBe("private, no-store");
    expect(privateManifest.json().items).toEqual([expect.objectContaining({ access_class: "private_user_asset", resource_id: privateId })]);
    expect(privateAsset.rawPayload).toEqual(Buffer.from("private"));
    expect(privateAsset.headers["cache-control"]).toBe("private, no-store");
    expect(deniedPrivate.statusCode).toBe(404);
    expect(controlledAdmin.statusCode).toBe(200);

    const publicGuess = await app.inject({ headers, method: "GET", url: `/api/v1/assets/public/${releaseVersion}/${privateId}` });
    expect(publicGuess.statusCode).toBe(404);
    test.revokePreview();
    const revoked = await app.inject({ headers: { ...headers, cookie: ownerCookie }, method: "GET", url: `/api/v1/assets/preview/${releaseVersion}/${previewId}` });
    expect(revoked.statusCode).toBe(404);
    expect(test.previewChecks()).toBe(4);

    evidence("response.json", {
      controlled_admin_status: controlledAdmin.statusCode,
      private_intruder_status: deniedPrivate.statusCode,
      public_guess_status: publicGuess.statusCode,
      revoked_preview_status: revoked.statusCode,
    });
    evidence("headers.json", {
      private: privateAsset.headers["cache-control"],
      preview: previewAsset.headers["cache-control"],
      public: publicAsset.headers["cache-control"],
    });
    await app.close();
  });
});
