import { afterEach, beforeEach, expect, test } from "bun:test";
import { mkdtempSync, rmSync } from "fs";
import { tmpdir } from "os";
import { join } from "path";
import type { OutboundAction } from "../../packages/butler-agent/src/test-support/harness/contracts.ts";
import { DeliveryGuard } from "../../packages/butler-agent/src/interfaces/transport/delivery-guard.ts";

let tempDir = "";
let originalButlerData: string | undefined;

beforeEach(() => {
  tempDir = mkdtempSync(join(tmpdir(), "butler-delivery-guard-"));
  originalButlerData = process.env.BUTLER_DATA;
  process.env.BUTLER_DATA = tempDir;
});

afterEach(() => {
  if (originalButlerData === undefined) delete process.env.BUTLER_DATA;
  else process.env.BUTLER_DATA = originalButlerData;
  rmSync(tempDir, { recursive: true, force: true });
});

const action: OutboundAction = {
  actionId: "report-1",
  transport: "mock",
  accountId: "default",
  peer: { kind: "dm", id: "user-1" },
  message: { text: "final report" },
};

test("delivery guard converts adapter exceptions into retryable delivery failures", async () => {
  let calls = 0;
  const guard = new DeliveryGuard({
    maxAttempts: 2,
    adapters: [{
      id: "mock",
      capabilities: {
        supportsThreads: false,
        supportsMessageEdit: false,
        supportsReactions: false,
        supportsAttachments: false,
        supportsStreamingEdits: false,
        supportsPresence: false,
      },
      async start() {},
      async send() {
        calls += 1;
        throw new Error("Telegram Bad Request: message is too long");
      },
    }],
  });

  const result = await guard.deliver("butler/main", action, { notificationId: "planned-report-1" });

  expect(calls).toBe(2);
  expect(result).toMatchObject({
    actionId: "report-1",
    ok: false,
    error: "Telegram Bad Request: message is too long",
  });
});

test("delivery guard deduplicates successful deliveries by session and action", async () => {
  let calls = 0;
  const guard = new DeliveryGuard({
    adapters: [{
      id: "mock",
      capabilities: {
        supportsThreads: false,
        supportsMessageEdit: false,
        supportsReactions: false,
        supportsAttachments: false,
        supportsStreamingEdits: false,
        supportsPresence: false,
      },
      async start() {},
      async send() {
        calls += 1;
        return { ok: true, transportMessageId: String(calls) };
      },
    }],
  });

  const first = await guard.deliver("butler/main", action);
  const second = await guard.deliver("butler/main", action);

  expect(first).toMatchObject({ ok: true, transportMessageId: "1" });
  expect(second).toMatchObject({ ok: true, duplicate: true });
  expect(calls).toBe(1);
});

test("delivery guard skips metadata-only activity for plain transports", async () => {
  let calls = 0;
  const guard = new DeliveryGuard({
    adapters: [{
      id: "mock",
      capabilities: {
        supportsThreads: false,
        supportsMessageEdit: false,
        supportsReactions: false,
        supportsAttachments: false,
        supportsStreamingEdits: false,
        supportsPresence: false,
      },
      async start() {},
      async send() {
        calls += 1;
        return { ok: true, transportMessageId: String(calls) };
      },
    }],
  });

  const result = await guard.deliver("butler/main", {
    actionId: "progress-1",
    transport: "mock",
    accountId: "default",
    peer: { kind: "dm", id: "user-1" },
    message: { text: "" },
    metadata: {
      kind: "tool_progress",
      toolName: "Bash",
      safeLabel: "Bash: bun test",
    },
  });

  expect(result).toMatchObject({ actionId: "progress-1", ok: true });
  expect(calls).toBe(0);
});

test("delivery guard skips metadata-only turn events for aggregate fallback transports", async () => {
  let calls = 0;
  const guard = new DeliveryGuard({
    adapters: [{
      id: "mock",
      capabilities: {
        supportsThreads: false,
        supportsMessageEdit: false,
        supportsReactions: false,
        supportsAttachments: false,
        supportsStreamingEdits: false,
        supportsPresence: false,
        supportsFinalAggregateOnly: true,
      },
      async start() {},
      async send() {
        calls += 1;
        return { ok: true, transportMessageId: String(calls) };
      },
    }],
  });

  const result = await guard.deliver("butler/main", {
    actionId: "turn-event-1",
    transport: "mock",
    accountId: "default",
    peer: { kind: "dm", id: "user-1" },
    message: { text: "" },
    metadata: {
      kind: "turn_event",
      eventKind: "guard.completed",
    },
  });

  expect(result).toMatchObject({ actionId: "turn-event-1", ok: true });
  expect(calls).toBe(0);
});
