import { describe, it, expect, beforeEach, afterEach } from "@jest/globals";
import { AuditLogService } from "./audit-log-service.js";
import { AuditLogPersistence } from "./audit-log-persistence.js";
import { AuditLog } from "../../model/audit-log-type.js";
import {
  ConfigAppliedPayload,
  ToolUsedPayload,
} from "../../model/audit-log-type.js";
import { systemClock } from "@mcpx/toolkit-core/time";
import { noOpLogger } from "@mcpx/toolkit-core/logging";
import { resetEnv } from "../../env.js";

export class InMemoryAuditLogPersistence implements AuditLogPersistence {
  private events: AuditLog[] = [];

  async persist(events: AuditLog[]): Promise<void> {
    this.events.push(...events);
  }

  async cleanup(): Promise<void> {
    // In-memory persistence doesn't need cleanup
  }

  getEvents(): AuditLog[] {
    return [...this.events];
  }

  clear(): void {
    this.events = [];
  }
}

describe("AuditLogService", () => {
  let auditLogService: AuditLogService;
  let persistence: InMemoryAuditLogPersistence;

  beforeEach(() => {
    // Set required environment variables for tests
    process.env["VERSION"] = "test-version";
    process.env["INSTANCE_ID"] = "test-instance";

    // Reset env to pick up the new values
    resetEnv();

    persistence = new InMemoryAuditLogPersistence();
    auditLogService = new AuditLogService(
      systemClock,
      noOpLogger,
      persistence,
      1000,
    ); // 1 second flush interval
  });

  afterEach(() => {
    // Restore original env
    delete process.env["VERSION"];
    delete process.env["INSTANCE_ID"];
    // Clear persistence to avoid test interference
    persistence.clear();
  });

  describe("buffer operations", () => {
    it("should add config applied events to buffer", async () => {
      const payload: ConfigAppliedPayload = {
        version: 1,
        config: {
          permissions: {
            default: { _type: "default-allow", block: [] },
            consumers: {},
            clientNames: {},
          },
          toolGroups: [],
          auth: { enabled: false },
          toolExtensions: { services: {} },
          targetServerAttributes: {},
        },
      };

      auditLogService.log({
        eventType: "config_applied",
        payload,
      });

      // Wait for flush to happen naturally (1s interval)
      await new Promise((resolve) => setTimeout(resolve, 1100));

      // Check that events were persisted
      const persistedEvents = persistence.getEvents();
      expect(persistedEvents).toHaveLength(1);
      expect(persistedEvents[0]?.payload).toEqual(payload);
    });

    it("should add tool used events to buffer", async () => {
      const payload: ToolUsedPayload = {
        toolName: "test_tool",
        targetServerName: "test_server",
        args: { param1: "value1", param2: 42 },
        consumerTag: "test_consumer",
      };

      auditLogService.log({
        eventType: "tool_used",
        payload,
      });

      // Wait for flush to happen naturally (1s interval)
      await new Promise((resolve) => setTimeout(resolve, 1100));

      // Check that events were persisted
      const persistedEvents = persistence.getEvents();
      expect(persistedEvents).toHaveLength(1);
      expect(persistedEvents[0]?.payload).toEqual(payload);
    });
  });

  describe("shutdown", () => {
    it("should flush all events on shutdown", async () => {
      const configPayload: ConfigAppliedPayload = {
        version: 1,
        config: {
          permissions: {
            default: { _type: "default-allow", block: [] },
            consumers: {},
            clientNames: {},
          },
          toolGroups: [],
          auth: { enabled: false },
          toolExtensions: { services: {} },
          targetServerAttributes: {},
        },
      };

      const toolPayload: ToolUsedPayload = {
        toolName: "test_tool",
        targetServerName: "test_server",
        args: { param1: "value1", param2: 42 },
        consumerTag: "test_consumer",
      };

      // Add both types of events to buffer
      auditLogService.log({
        eventType: "config_applied",
        payload: configPayload,
      });

      auditLogService.log({
        eventType: "tool_used",
        payload: toolPayload,
      });

      // Wait for flush to happen naturally (1s interval)
      await new Promise((resolve) => setTimeout(resolve, 1100));

      await auditLogService.shutdown();

      // Check that all events were persisted
      const persistedEvents = persistence.getEvents();
      expect(persistedEvents).toHaveLength(2);
      expect(persistedEvents).toEqual(
        expect.arrayContaining([
          expect.objectContaining({ payload: configPayload }),
          expect.objectContaining({ payload: toolPayload }),
        ]),
      );
    });
  });
});
