import { describe, expect, it } from 'vitest';
import type { CommandDef, SlashCommandPayload } from '@mosaicstack/types';
import { CommandAuthorizationService } from './command-authorization.service.js';

const adminCommand: CommandDef = {
  name: 'gc',
  description: 'GC',
  aliases: [],
  scope: 'admin',
  execution: 'socket',
  available: true,
};
const payload: SlashCommandPayload = { command: 'gc', conversationId: 'conversation-1' };

function createService(
  role: string,
  entries: Map<string, string> = new Map<string, string>(),
): CommandAuthorizationService {
  const db = {
    select: () => ({ from: () => ({ where: () => ({ limit: async () => [{ role }] }) }) }),
  };
  const redis = {
    get: async (key: string) => entries.get(key) ?? null,
    set: async (key: string, value: string) => {
      entries.set(key, value);
    },
    del: async (key: string) => Number(entries.delete(key)),
  };
  return new CommandAuthorizationService(db as never, redis);
}

describe('CommandAuthorizationService', () => {
  it('consumes one exact actor-bound approval once', async (): Promise<void> => {
    const service = createService('admin');
    const approval = await service.createApproval(adminCommand, payload, 'admin-1');
    expect(approval).not.toBeNull();
    expect(
      (await service.authorize(adminCommand, payload, 'admin-1', approval!.approvalId)).allowed,
    ).toBe(true);
    expect(
      (await service.authorize(adminCommand, payload, 'admin-1', approval!.approvalId)).allowed,
    ).toBe(false);
  });

  it('rejects an approval when the structured action is mutated', async (): Promise<void> => {
    const service = createService('admin');
    const approval = await service.createApproval(adminCommand, payload, 'admin-1');
    const mutated = { ...payload, conversationId: 'other-conversation' };
    expect(approval).not.toBeNull();
    expect(
      (await service.authorize(adminCommand, mutated, 'admin-1', approval!.approvalId)).allowed,
    ).toBe(false);
  });

  it('denies an admin command to a member before approval is considered', async (): Promise<void> => {
    const service = createService('member');
    const approval = await service.createApproval(adminCommand, payload, 'member-1');
    expect(approval).toBeNull();
    expect(
      (await service.authorize(adminCommand, payload, 'member-1', 'forged-approval-id')).allowed,
    ).toBe(false);
  });

  it('denies a malformed durable approval expiry instead of treating it as unexpired', async (): Promise<void> => {
    const entries = new Map<string, string>();
    const action = {
      providerId: 'fleet',
      sessionId: 'nova',
      actorId: 'admin-1',
      tenantId: 'tenant-1',
      channelId: 'discord:operator',
      correlationId: 'correlation-malformed-expiry',
      agentName: 'Nova',
    };
    const service = createService('admin', entries);
    const approval = await service.createRuntimeTerminationApproval(action);
    expect(approval).not.toBeNull();
    const key = `agent:Nova:command-approval:${approval!.approvalId}`;
    const stored = entries.get(key);
    expect(stored).toBeDefined();
    entries.set(key, JSON.stringify({ ...JSON.parse(stored!), expiresAt: 'not-a-date' }));

    expect(await service.consumeRuntimeTerminationApproval(approval!.approvalId, action)).toBe(
      false,
    );
  });

  it('persists and consumes one exact runtime termination approval across a service restart', async (): Promise<void> => {
    const entries = new Map<string, string>();
    const action = {
      providerId: 'fleet',
      sessionId: 'nova',
      actorId: 'admin-1',
      tenantId: 'tenant-1',
      channelId: 'discord:operator',
      correlationId: 'correlation-1',
      agentName: 'Nova',
    };
    const beforeRestart = createService('admin', entries);
    const approval = await beforeRestart.createRuntimeTerminationApproval(action);

    const afterRestart = createService('admin', entries);
    expect(
      await afterRestart.consumeRuntimeTerminationApproval(approval!.approvalId, {
        ...action,
        sessionId: 'forged-session',
      }),
    ).toBe(false);
    expect(await afterRestart.consumeRuntimeTerminationApproval(approval!.approvalId, action)).toBe(
      true,
    );
    expect(await afterRestart.consumeRuntimeTerminationApproval(approval!.approvalId, action)).toBe(
      false,
    );
  });
});
