/**
 * @license
 * Copyright 2026 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import {
  vi,
  describe,
  it,
  expect,
  beforeEach,
  afterEach,
  type MockInstance,
} from 'vitest';
import { type Config } from '@google/gemini-cli-core';
import { handleList, listCommand } from './list.js';
import { loadSettings, type LoadedSettings } from '../../config/settings.js';
import { loadCliConfig } from '../../config/config.js';
import chalk from 'chalk';

vi.mock('@google/gemini-cli-core', async (importOriginal) => {
  const { mockCoreDebugLogger } = await import(
    '../../test-utils/mockDebugLogger.js'
  );
  return mockCoreDebugLogger(
    await importOriginal<typeof import('@google/gemini-cli-core')>(),
    {
      stripAnsi: false,
    },
  );
});

vi.mock('../../config/settings.js');
vi.mock('../../config/config.js');
vi.mock('../utils.js', () => ({
  exitCli: vi.fn(),
}));

describe('skills list command', () => {
  const mockLoadSettings = vi.mocked(loadSettings);
  const mockLoadCliConfig = vi.mocked(loadCliConfig);
  let stdoutWriteSpy: MockInstance<typeof process.stdout.write>;

  beforeEach(async () => {
    vi.clearAllMocks();
    mockLoadSettings.mockReturnValue({
      merged: {},
    } as unknown as LoadedSettings);
    stdoutWriteSpy = vi
      .spyOn(process.stdout, 'write')
      .mockImplementation(() => true);
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('handleList', () => {
    it('should log a message if no skills are discovered', async () => {
      const mockConfig = {
        initialize: vi.fn().mockResolvedValue(undefined),
        getSkillManager: vi.fn().mockReturnValue({
          getAllSkills: vi.fn().mockReturnValue([]),
        }),
      };
      mockLoadCliConfig.mockResolvedValue(mockConfig as unknown as Config);

      await handleList({});

      expect(stdoutWriteSpy).toHaveBeenCalledWith('No skills discovered.\n');
    });

    it('should list all discovered skills', async () => {
      const skills = [
        {
          name: 'skill1',
          description: 'desc1',
          disabled: false,
          location: '/path/to/skill1',
        },
        {
          name: 'skill2',
          description: 'desc2',
          disabled: true,
          location: '/path/to/skill2',
        },
      ];
      const mockConfig = {
        initialize: vi.fn().mockResolvedValue(undefined),
        getSkillManager: vi.fn().mockReturnValue({
          getAllSkills: vi.fn().mockReturnValue(skills),
        }),
      };
      mockLoadCliConfig.mockResolvedValue(mockConfig as unknown as Config);

      await handleList({});

      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        chalk.bold('Discovered Agent Skills:') + '\n\n',
      );
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining('skill1'),
      );
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining(chalk.green('[Enabled]')),
      );
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining('skill2'),
      );
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining(chalk.red('[Disabled]')),
      );
    });

    it('should filter built-in skills by default and show them with { all: true }', async () => {
      const skills = [
        {
          name: 'regular',
          description: 'desc1',
          disabled: false,
          location: '/loc1',
        },
        {
          name: 'builtin',
          description: 'desc2',
          disabled: false,
          location: '/loc2',
          isBuiltin: true,
        },
      ];
      const mockConfig = {
        initialize: vi.fn().mockResolvedValue(undefined),
        getSkillManager: vi.fn().mockReturnValue({
          getAllSkills: vi.fn().mockReturnValue(skills),
        }),
      };
      mockLoadCliConfig.mockResolvedValue(mockConfig as unknown as Config);

      // Default
      await handleList({ all: false });
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining('regular'),
      );
      expect(stdoutWriteSpy).not.toHaveBeenCalledWith(
        expect.stringContaining('builtin'),
      );

      vi.clearAllMocks();

      // With all: true
      await handleList({ all: true });
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining('regular'),
      );
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining('builtin'),
      );
      expect(stdoutWriteSpy).toHaveBeenCalledWith(
        expect.stringContaining(chalk.gray(' [Built-in]')),
      );
    });

    it('should throw an error when listing fails', async () => {
      mockLoadCliConfig.mockRejectedValue(new Error('List failed'));

      await expect(handleList({})).rejects.toThrow('List failed');
    });
  });

  describe('listCommand', () => {
    const command = listCommand;

    it('should have correct command and describe', () => {
      expect(command.command).toBe('list [--all]');
      expect(command.describe).toBe('Lists discovered agent skills.');
    });
  });
});
