import { describe, it, expect, vi } from 'vitest';

import { createApproveCommand, createApprovalsCommand, createBackendCommand, createContextCommand, createCouncilCommand, createDenyCommand, createElevateCommand, createModelCommand, createQueueCommand, createResearchCommand, createSkillCommand, createStopCommand, createToolsCommand, createTransferCommand } from './index.js';

describe('builtin /model command', () => {
  it('passes through the full argument string', async () => {
    const cmd = createModelCommand();
    const setModel = vi.fn(() => 'ok');

    const result = await cmd.execute(['default', 'github/gpt-5-mini'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/model default github/gpt-5-mini',
      services: { setModel },
    });

    expect(setModel).toHaveBeenCalledWith('default github/gpt-5-mini');
    expect(result).toEqual({ handled: true, text: 'ok' });
  });

  it('still works for single-argument tier switching', async () => {
    const cmd = createModelCommand();
    const setModel = vi.fn(() => 'switched');

    const result = await cmd.execute(['fast'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/model fast',
      services: { setModel },
    });

    expect(setModel).toHaveBeenCalledWith('fast');
    expect(result).toEqual({ handled: true, text: 'switched' });
  });
});

describe('builtin /research command', () => {
  it('delegates to the research agent with the full task string', async () => {
    const cmd = createResearchCommand();
    const delegateAgent = vi.fn(() => 'research output');

    const result = await cmd.execute(['compare', 'k0s', 'vs', 'k3s'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/research compare k0s vs k3s',
      services: { delegateAgent },
    });

    expect(delegateAgent).toHaveBeenCalledWith('research', 'compare k0s vs k3s');
    expect(result).toEqual({ handled: true, text: 'research output' });
  });

  it('returns usage when no task is provided', async () => {
    const cmd = createResearchCommand();
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/research',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Usage: /research <question or task>' });
  });
});

describe('builtin /council command', () => {
  it('passes the full task to runCouncil', async () => {
    const cmd = createCouncilCommand();
    const runCouncil = vi.fn(() => 'council output');
    const result = await cmd.execute(['design', 'a', 'backup', 'strategy'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/council design a backup strategy',
      services: { runCouncil },
    });
    expect(runCouncil).toHaveBeenCalledWith('design a backup strategy');
    expect(result).toEqual({ handled: true, text: 'council output' });
  });

  it('returns usage when no task is provided', async () => {
    const cmd = createCouncilCommand();
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/council',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Usage: /council <question or task> | /council preflight' });
  });
});

describe('builtin /elevate command', () => {
  it('passes through the full argument string', async () => {
    const cmd = createElevateCommand();
    const setElevation = vi.fn(() => 'ok');

    const result = await cmd.execute(['10m', 'reason', '--yes'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/elevate 10m reason --yes',
      services: { setElevation },
    });

    expect(setElevation).toHaveBeenCalledWith('10m reason --yes');
    expect(result).toEqual({ handled: true, text: 'ok' });
  });

  it('shows status when no args are provided', async () => {
    const cmd = createElevateCommand();
    const getElevation = vi.fn(() => 'status');
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/elevate',
      services: { getElevation },
    });
    expect(getElevation).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: 'status' });
  });
});

describe('builtin /queue command', () => {
  it('shows queue status with no args', async () => {
    const cmd = createQueueCommand();
    const getQueue = vi.fn(() => 'queue status');
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/queue',
      services: { getQueue },
    });
    expect(getQueue).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: 'queue status' });
  });

  it('passes through set operations', async () => {
    const cmd = createQueueCommand();
    const setQueue = vi.fn(() => 'updated');
    const result = await cmd.execute(['set', 'mode', 'followup'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/queue set mode followup',
      services: { setQueue },
    });
    expect(setQueue).toHaveBeenCalledWith('mode followup');
    expect(result).toEqual({ handled: true, text: 'updated' });
  });

  it('calls reset operation', async () => {
    const cmd = createQueueCommand();
    const resetQueue = vi.fn(() => 'reset');
    const result = await cmd.execute(['reset'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/queue reset',
      services: { resetQueue },
    });
    expect(resetQueue).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: 'reset' });
  });
});

describe('builtin /context command', () => {
  it('returns context usage text from services', async () => {
    const cmd = createContextCommand();
    const getContext = vi.fn(() => 'Context Usage\n\n75.0%');
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/context',
      services: { getContext },
    });
    expect(getContext).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: 'Context Usage\n\n75.0%' });
  });

  it('returns not-available when service is missing', async () => {
    const cmd = createContextCommand();
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/context',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Context command is not available in this session.' });
  });
});

describe('builtin /tools command', () => {
  it('returns tools text from services', async () => {
    const cmd = createToolsCommand();
    const getTools = vi.fn(() => 'Available tools (2):\n- file.read\n- web.fetch');
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/tools',
      services: { getTools },
    });
    expect(getTools).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: 'Available tools (2):\n- file.read\n- web.fetch' });
  });

  it('returns not-available when service is missing', async () => {
    const cmd = createToolsCommand();
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/tools',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Tools command is not available in this session.' });
  });
});

describe('builtin /transfer command', () => {
  it('passes through the full target argument string', async () => {
    const cmd = createTransferCommand();
    const transferSession = vi.fn(() => 'Session transferred');
    const result = await cmd.execute(['telegram'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/transfer telegram',
      services: { transferSession },
    });
    expect(transferSession).toHaveBeenCalledWith('telegram');
    expect(result).toEqual({ handled: true, text: 'Session transferred' });
  });

  it('returns not-available when service is missing', async () => {
    const cmd = createTransferCommand();
    const result = await cmd.execute(['tui'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/transfer tui',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Transfer command is not available in this session.' });
  });
});

describe('builtin /stop command', () => {
  it('calls cancelRun service', async () => {
    const cmd = createStopCommand();
    const cancelRun = vi.fn(() => 'Cancellation requested.');
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/stop',
      services: { cancelRun },
    });
    expect(cancelRun).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: 'Cancellation requested.' });
  });

  it('returns not-available when service is missing', async () => {
    const cmd = createStopCommand();
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/stop',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Stop command is not available in this session.' });
  });
});

describe('builtin approval commands', () => {
  it('calls getApprovals for /approvals', async () => {
    const cmd = createApprovalsCommand();
    const getApprovals = vi.fn(() => '1 pending');
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/approvals',
      services: { getApprovals },
    });
    expect(getApprovals).toHaveBeenCalledOnce();
    expect(result).toEqual({ handled: true, text: '1 pending' });
  });

  it('passes raw input to approvePending for /approve', async () => {
    const cmd = createApproveCommand();
    const approvePending = vi.fn(() => 'approved');
    const result = await cmd.execute(['abc-123'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/approve abc-123',
      services: { approvePending },
    });
    expect(approvePending).toHaveBeenCalledWith('abc-123');
    expect(result).toEqual({ handled: true, text: 'approved' });
  });

  it('passes raw input to denyPending for /deny', async () => {
    const cmd = createDenyCommand();
    const denyPending = vi.fn(() => 'denied');
    const result = await cmd.execute(['abc-123', 'too', 'risky'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/deny abc-123 too risky',
      services: { denyPending },
    });
    expect(denyPending).toHaveBeenCalledWith('abc-123 too risky');
    expect(result).toEqual({ handled: true, text: 'denied' });
  });
});

describe('builtin /skill command', () => {
  it('passes subcommand text to skillCommand service', async () => {
    const cmd = createSkillCommand();
    const skillCommand = vi.fn(() => 'ok');
    const result = await cmd.execute(['search', 'calendar'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/skill search calendar',
      services: { skillCommand },
    });
    expect(skillCommand).toHaveBeenCalledWith('search calendar');
    expect(result).toEqual({ handled: true, text: 'ok' });
  });
});

describe('builtin /backend command', () => {
  it('registers /runtime as an alias', () => {
    const cmd = createBackendCommand();
    expect(cmd.aliases).toContain('runtime');
  });

  it('passes subcommand input to backendCommand service', async () => {
    const cmd = createBackendCommand();
    const backendCommand = vi.fn(() => 'Pi backend deactivated.');
    const result = await cmd.execute(['deactivate', 'pi'], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/backend deactivate pi',
      services: { backendCommand },
    });

    expect(backendCommand).toHaveBeenCalledWith('deactivate pi');
    expect(result).toEqual({ handled: true, text: 'Pi backend deactivated.' });
  });

  it('returns not-available when service is missing', async () => {
    const cmd = createBackendCommand();
    const result = await cmd.execute([], {
      channel: 'test',
      senderId: 'user',
      sessionId: 's1',
      rawInput: '/backend',
      services: {},
    });
    expect(result).toEqual({ handled: true, text: 'Backend command is not available in this session.' });
  });
});
