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

const registry = {
  getManifest: vi.fn(() => ({
    version: 1,
    commands: [
      {
        name: 'gc',
        description: 'System-wide garbage collection',
        aliases: [],
        scope: 'admin' as const,
        execution: 'socket' as const,
        available: true,
      },
    ],
    skills: [],
  })),
};

const sessionGc = {
  sweepOrphans: vi.fn().mockResolvedValue({ orphanedSessions: 1, totalCleaned: [], duration: 1 }),
};

const scope = (userId: string) => ({ userId, tenantId: 'tenant-1' });

const authorization = {
  authorize: vi.fn((_command: unknown, _payload: unknown, actorId: string) =>
    Promise.resolve(
      actorId === 'member-1'
        ? { allowed: false, reason: 'durable approval is required' }
        : { allowed: false, reason: 'not authorized for this command scope' },
    ),
  ),
};

function buildExecutor(authorizationService: unknown = authorization): CommandExecutorService {
  return new CommandExecutorService(
    registry as never,
    { getSession: vi.fn() } as never,
    { clear: vi.fn(), set: vi.fn() } as never,
    sessionGc as never,
    { set: vi.fn() } as never,
    { agents: {} } as never,
    null,
    null,
    null,
    authorizationService as never,
  );
}

function createDurableAuthorization(): CommandAuthorizationService {
  const entries = new Map<string, string>();
  const db = {
    select: () => ({ from: () => ({ where: () => ({ limit: async () => [{ role: 'admin' }] }) }) }),
  };
  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('TESS-M1-SEC-001 command authorization abuse cases', () => {
  const payload: SlashCommandPayload = { command: 'gc', conversationId: 'conversation-1' };

  beforeEach((): void => {
    vi.clearAllMocks();
  });

  it('denies a forged admin identity and does not execute a system-wide command', async (): Promise<void> => {
    const result = await buildExecutor().execute(payload, scope('admin-forged-by-client'));

    expect(result.success).toBe(false);
    expect(result.message).toContain('not authorized');
    expect(sessionGc.sweepOrphans).not.toHaveBeenCalled();
  });

  it('denies a privileged command without a server-bound durable approval', async (): Promise<void> => {
    const result = await buildExecutor().execute(payload, scope('member-1'));

    expect(result.success).toBe(false);
    expect(result.message).toContain('approval');
    expect(sessionGc.sweepOrphans).not.toHaveBeenCalled();
  });

  it('executes an admin command only after a valid durable approval is issued and supplied', async (): Promise<void> => {
    const executor = buildExecutor(createDurableAuthorization());

    const adminScope = scope('admin-1');
    const denied = await executor.execute(payload, adminScope);
    const approval = await executor.createApproval(payload, adminScope);
    const approved = await executor.execute(
      { ...payload, approvalId: approval?.approvalId },
      adminScope,
    );

    expect(denied.success).toBe(false);
    expect(denied.message).toContain('approval');
    expect(approval).not.toBeNull();
    // A valid durable approval is consumed, but cannot authorize an unimplemented
    // global retention operation. Session-scoped cleanup remains lifecycle-only.
    expect(approved.success).toBe(false);
    expect(approved.message).toContain('Global GC is disabled');
    expect(sessionGc.sweepOrphans).not.toHaveBeenCalled();
  });
});
