import { describe, it, expect } from "vitest";
import { createExportHandler } from "@/handlers/export-handler";
import { createDeleteHandler } from "@/handlers/delete-handler";
import { createRectifyHandler } from "@/handlers/rectify-handler";
import { createRestrictHandler } from "@/handlers/restrict-handler";
import type { IDataExport } from "@/data-export.interface";
import type { IDataDelete } from "@/data-delete.interface";
import type { IDataRectify } from "@/data-rectify.interface";
import type { IProcessingRestriction } from "@/processing-restriction.interface";
import {
  RecordingDataExport,
  RecordingDataDelete,
  RecordingDataRectify,
  RecordingProcessingRestriction,
} from "@repo/core-testing/instrumentation";

// Recording doubles use local type aliases to avoid circular deps with core-dsr.
// Cast via unknown so they satisfy the interface at the TypeScript level while
// remaining structurally compatible at runtime.

describe("createExportHandler", () => {
  it("calls exportSubjectData and returns status 200 with bundle body", async () => {
    const dataExport = new RecordingDataExport();
    const handler = createExportHandler(dataExport as unknown as IDataExport);
    const res = await handler({ subjectId: "alice", format: "json" });

    expect(res.status).toBe(200);
    expect(res.body.subjectId).toBe("alice");
    expect(res.body.format).toBe("json");
    expect(dataExport.calls).toHaveLength(1);
    expect(dataExport.calls[0]).toEqual({ subjectId: "alice", format: "json" });
  });

  it("sets Content-Type: application/json for json format", async () => {
    const dataExport = new RecordingDataExport();
    const handler = createExportHandler(dataExport as unknown as IDataExport);
    const res = await handler({ subjectId: "alice", format: "json" });
    expect(res.headers?.["Content-Type"]).toBe("application/json");
  });

  it("sets Content-Type: application/ld+json for json-ld format", async () => {
    const dataExport = new RecordingDataExport();
    const handler = createExportHandler(dataExport as unknown as IDataExport);
    const res = await handler({ subjectId: "alice", format: "json-ld" });
    expect(res.headers?.["Content-Type"]).toBe("application/ld+json");
  });
});

describe("createDeleteHandler", () => {
  it("calls deleteSubjectData and returns status 200 with certificate body", async () => {
    const dataDelete = new RecordingDataDelete();
    const handler = createDeleteHandler(dataDelete as unknown as IDataDelete);
    const res = await handler({ subjectId: "alice", mode: "soft" });

    expect(res.status).toBe(200);
    expect(res.body.subjectId).toBe("alice");
    expect(res.body.mode).toBe("soft");
    expect(dataDelete.calls).toHaveLength(1);
    expect(dataDelete.calls[0]).toEqual({ subjectId: "alice", mode: "soft" });
  });

  it("passes cascade-hard mode to deleteSubjectData", async () => {
    const dataDelete = new RecordingDataDelete();
    const handler = createDeleteHandler(dataDelete as unknown as IDataDelete);
    const res = await handler({ subjectId: "alice", mode: "cascade-hard" });
    expect(res.body.mode).toBe("cascade-hard");
    expect(dataDelete.calls[0]?.mode).toBe("cascade-hard");
  });
});

describe("createRectifyHandler", () => {
  it("calls updateSubjectField and returns status 200 with ok body", async () => {
    const dataRectify = new RecordingDataRectify();
    const handler = createRectifyHandler(
      dataRectify as unknown as IDataRectify,
    );
    const res = await handler({
      subjectId: "alice",
      collection: "users",
      field: "name",
      value: "Alice New",
    });

    expect(res.status).toBe(200);
    expect(res.body).toEqual({ ok: true });
    expect(dataRectify.calls).toHaveLength(1);
    expect(dataRectify.calls[0]).toEqual({
      subjectId: "alice",
      collection: "users",
      field: "name",
      value: "Alice New",
    });
  });
});

describe("createRestrictHandler", () => {
  it("calls setRestriction with granted=true and returns status 200", async () => {
    const processingRestriction = new RecordingProcessingRestriction();
    const handler = createRestrictHandler(
      processingRestriction as unknown as IProcessingRestriction,
    );
    const res = await handler({ subjectId: "alice", granted: true });

    expect(res.status).toBe(200);
    expect(res.body).toEqual({ ok: true });
    expect(processingRestriction.sets).toHaveLength(1);
    expect(processingRestriction.sets[0]).toEqual({
      subjectId: "alice",
      granted: true,
    });
  });

  it("calls setRestriction with granted=false", async () => {
    const processingRestriction = new RecordingProcessingRestriction();
    const handler = createRestrictHandler(
      processingRestriction as unknown as IProcessingRestriction,
    );
    await handler({ subjectId: "alice", granted: false });
    expect(processingRestriction.sets[0]).toEqual({
      subjectId: "alice",
      granted: false,
    });
  });
});
