import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { resolve, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { mkdir, writeFile, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { resetCounter } from '../../scanners/lib/output.mjs';
import { scan } from '../../scanners/collision-scanner.mjs';

const __dirname = fileURLToPath(new URL('.', import.meta.url));
const FIXTURES = resolve(__dirname, '../fixtures');
const COLLISION_FIXTURE_HOME = resolve(FIXTURES, 'collision-plugins', 'fake-home');

function uniqueDir(suffix) {
  return join(tmpdir(), `config-audit-col-${suffix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
}

/**
 * The COL scanner uses process.env.HOME via enumeratePlugins/enumerateSkills.
 * Tests must override HOME, run, and restore — never rely on user-state.
 */
async function runScannerWithHome(home) {
  resetCounter();
  const original = process.env.HOME;
  process.env.HOME = home;
  try {
    return await scan('/unused', { files: [] });
  } finally {
    process.env.HOME = original;
  }
}

describe('COL scanner — basic structure', () => {
  it('reports scanner prefix COL', async () => {
    const result = await runScannerWithHome(COLLISION_FIXTURE_HOME);
    assert.equal(result.scanner, 'COL');
  });

  it('finding IDs match CA-COL-NNN pattern', async () => {
    const result = await runScannerWithHome(COLLISION_FIXTURE_HOME);
    for (const f of result.findings) {
      assert.match(f.id, /^CA-COL-\d{3}$/);
    }
  });
});

describe('COL scanner — user-vs-plugin collision (medium severity)', () => {
  it('flags review skill collision between user-level and plugin-bundled', async () => {
    const result = await runScannerWithHome(COLLISION_FIXTURE_HOME);
    const f = result.findings.find(x => /user-level and plugin sources/i.test(x.title || ''));
    assert.ok(f, `expected user-vs-plugin finding; got: ${result.findings.map(x => x.title).join(' | ')}`);
    assert.equal(f.severity, 'medium', `expected medium, got ${f.severity}`);
    assert.match(String(f.title), /review/);
  });

  it('user-vs-plugin finding includes details.namespaces', async () => {
    const result = await runScannerWithHome(COLLISION_FIXTURE_HOME);
    const f = result.findings.find(x => /user-level and plugin sources/i.test(x.title || ''));
    assert.ok(f);
    assert.ok(Array.isArray(f.details?.namespaces),
      `expected details.namespaces array; got: ${JSON.stringify(f.details)}`);
    assert.ok(f.details.namespaces.length >= 2);
    const sources = f.details.namespaces.map(n => n.source);
    assert.ok(sources.includes('user'), `expected user in sources; got: ${sources.join(', ')}`);
  });
});

describe('COL scanner — negative cases', () => {
  it('plugin-c summarize (unique name) generates no finding', async () => {
    const result = await runScannerWithHome(COLLISION_FIXTURE_HOME);
    const f = result.findings.find(x => /summarize/i.test(x.title || ''));
    assert.equal(f, undefined,
      `expected no finding for unique plugin-c summarize skill; got: ${f?.title}`);
  });

  it('clean fake-home with no plugins yields zero findings', async () => {
    const cleanHome = uniqueDir('clean');
    try {
      await mkdir(join(cleanHome, '.claude', 'plugins'), { recursive: true });
      const result = await runScannerWithHome(cleanHome);
      assert.equal(result.findings.length, 0,
        `expected 0 findings; got: ${result.findings.map(f => f.title).join(' | ')}`);
    } finally {
      await rm(cleanHome, { recursive: true, force: true });
    }
  });
});

describe('COL scanner — plugin-vs-plugin (low severity, no user-level competitor)', () => {
  let altHome;

  beforeEach(async () => {
    altHome = uniqueDir('plugin-only');
    const root = join(altHome, '.claude', 'plugins', 'marketplaces', 'mp', 'plugins');
    await mkdir(join(root, 'plugin-x', '.claude-plugin'), { recursive: true });
    await writeFile(
      join(root, 'plugin-x', '.claude-plugin', 'plugin.json'),
      JSON.stringify({ name: 'plugin-x', version: '1.0.0', description: 'x' }),
    );
    await mkdir(join(root, 'plugin-x', 'skills', 'analyze'), { recursive: true });
    await writeFile(
      join(root, 'plugin-x', 'skills', 'analyze', 'SKILL.md'),
      '---\nname: x:analyze\ndescription: analyze from x\n---\nBody.\n',
    );
    await mkdir(join(root, 'plugin-y', '.claude-plugin'), { recursive: true });
    await writeFile(
      join(root, 'plugin-y', '.claude-plugin', 'plugin.json'),
      JSON.stringify({ name: 'plugin-y', version: '1.0.0', description: 'y' }),
    );
    await mkdir(join(root, 'plugin-y', 'skills', 'analyze'), { recursive: true });
    await writeFile(
      join(root, 'plugin-y', 'skills', 'analyze', 'SKILL.md'),
      '---\nname: y:analyze\ndescription: analyze from y\n---\nBody.\n',
    );
  });

  afterEach(async () => {
    if (altHome) await rm(altHome, { recursive: true, force: true });
  });

  it('plugin-x and plugin-y both define analyze → finding (low severity)', async () => {
    const result = await runScannerWithHome(altHome);
    const f = result.findings.find(x => /multiple plugins/i.test(x.title || ''));
    assert.ok(f, `expected plugin-vs-plugin finding; got: ${result.findings.map(x => x.title).join(' | ')}`);
    assert.equal(f.severity, 'low', `expected low, got ${f.severity}`);
    assert.match(String(f.title), /analyze/);
    assert.ok(Array.isArray(f.details?.namespaces));
    assert.equal(f.details.namespaces.length, 2);
  });
});

describe('COL scanner — suppression compatibility', () => {
  it('CA-COL-001 is NOT matched by CA-TOK-* glob suppression', async () => {
    const { applySuppressions } = await import('../../scanners/lib/suppression.mjs');
    const result = await runScannerWithHome(COLLISION_FIXTURE_HOME);
    assert.ok(result.findings.length > 0, 'precondition: at least one COL finding to test against');
    // Apply CA-TOK-* glob suppression — should leave COL findings untouched.
    const { active, suppressed } = applySuppressions(result.findings, [
      { pattern: 'CA-TOK-*', source: 'test', sourceLine: 1 },
    ]);
    assert.equal(active.length, result.findings.length,
      'CA-TOK-* glob should not match CA-COL-* findings');
    assert.equal(suppressed.length, 0);
  });
});
