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

import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import * as os from 'node:os';
import { FolderTrustDiscoveryService } from './FolderTrustDiscoveryService.js';
import { GEMINI_DIR } from '../utils/paths.js';

describe('FolderTrustDiscoveryService', () => {
  let tempDir: string;

  beforeEach(async () => {
    tempDir = await fs.mkdtemp(
      path.join(os.tmpdir(), 'gemini-discovery-test-'),
    );
  });

  afterEach(async () => {
    vi.restoreAllMocks();
    await fs.rm(tempDir, { recursive: true, force: true });
  });

  it('should discover commands, skills, mcps, and hooks', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });

    // Mock commands
    const commandsDir = path.join(geminiDir, 'commands');
    await fs.mkdir(commandsDir);
    await fs.writeFile(
      path.join(commandsDir, 'test-cmd.toml'),
      'prompt = "test"',
    );

    // Mock skills
    const skillsDir = path.join(geminiDir, 'skills');
    await fs.mkdir(path.join(skillsDir, 'test-skill'), { recursive: true });
    await fs.writeFile(path.join(skillsDir, 'test-skill', 'SKILL.md'), 'body');

    // Mock agents
    const agentsDir = path.join(geminiDir, 'agents');
    await fs.mkdir(agentsDir);
    await fs.writeFile(path.join(agentsDir, 'test-agent.md'), 'body');

    // Mock settings (MCPs, Hooks, and general settings)
    const settings = {
      mcpServers: {
        'test-mcp': { command: 'node', args: ['test.js'] },
      },
      hooks: {
        BeforeTool: [{ command: 'test-hook' }],
      },
      general: { vimMode: true },
      ui: { theme: 'Dark' },
    };
    await fs.writeFile(
      path.join(geminiDir, 'settings.json'),
      JSON.stringify(settings),
    );

    const results = await FolderTrustDiscoveryService.discover(tempDir);

    expect(results.commands).toContain('test-cmd');
    expect(results.skills).toContain('test-skill');
    expect(results.agents).toContain('test-agent');
    expect(results.mcps).toContain('test-mcp');
    expect(results.hooks).toContain('test-hook');
    expect(results.settings).toContain('general');
    expect(results.settings).toContain('ui');
    expect(results.settings).not.toContain('mcpServers');
    expect(results.settings).not.toContain('hooks');
  });

  it('should flag security warnings for sensitive settings', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });

    const settings = {
      tools: {
        allowed: ['git'],
        sandbox: false,
      },
      security: {
        folderTrust: {
          enabled: false,
        },
      },
    };
    await fs.writeFile(
      path.join(geminiDir, 'settings.json'),
      JSON.stringify(settings),
    );

    const results = await FolderTrustDiscoveryService.discover(tempDir);

    expect(results.securityWarnings).toContain(
      'This project auto-approves certain tools (tools.allowed).',
    );
    expect(results.securityWarnings).toContain(
      'This project attempts to disable folder trust (security.folderTrust.enabled).',
    );
    expect(results.securityWarnings).toContain(
      'This project disables the security sandbox (tools.sandbox).',
    );
  });

  it('should handle missing .gemini directory', async () => {
    const results = await FolderTrustDiscoveryService.discover(tempDir);
    expect(results.commands).toHaveLength(0);
    expect(results.skills).toHaveLength(0);
    expect(results.mcps).toHaveLength(0);
    expect(results.hooks).toHaveLength(0);
    expect(results.settings).toHaveLength(0);
  });

  it('should handle malformed settings.json', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });
    await fs.writeFile(path.join(geminiDir, 'settings.json'), 'invalid json');

    const results = await FolderTrustDiscoveryService.discover(tempDir);
    expect(results.discoveryErrors[0]).toContain(
      'Failed to discover settings: Unexpected token',
    );
  });

  it('should handle null settings.json', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });
    await fs.writeFile(path.join(geminiDir, 'settings.json'), 'null');

    const results = await FolderTrustDiscoveryService.discover(tempDir);
    expect(results.discoveryErrors).toHaveLength(0);
    expect(results.settings).toHaveLength(0);
  });

  it('should handle array settings.json', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });
    await fs.writeFile(path.join(geminiDir, 'settings.json'), '[]');

    const results = await FolderTrustDiscoveryService.discover(tempDir);
    expect(results.discoveryErrors).toHaveLength(0);
    expect(results.settings).toHaveLength(0);
  });

  it('should handle string settings.json', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });
    await fs.writeFile(path.join(geminiDir, 'settings.json'), '"string"');

    const results = await FolderTrustDiscoveryService.discover(tempDir);
    expect(results.discoveryErrors).toHaveLength(0);
    expect(results.settings).toHaveLength(0);
  });

  it('should flag security warning for custom agents', async () => {
    const geminiDir = path.join(tempDir, GEMINI_DIR);
    await fs.mkdir(geminiDir, { recursive: true });

    const agentsDir = path.join(geminiDir, 'agents');
    await fs.mkdir(agentsDir);
    await fs.writeFile(path.join(agentsDir, 'test-agent.md'), 'body');

    const results = await FolderTrustDiscoveryService.discover(tempDir);

    expect(results.agents).toContain('test-agent');
    expect(results.securityWarnings).toContain(
      'This project contains custom agents.',
    );
  });
});
