import path from 'path';
import os from 'os';
import { promises as fs } from 'fs';
import { afterEach, it, expect, vi } from 'vitest';
import type { Mock } from 'vitest';

vi.mock('../../src/services/archiveReader', () => ({ listArchiveEntries: vi.fn() }));

import scanDirectory from '../../src/services/fsScanner';
import { listArchiveEntries } from '../../src/services/archiveReader';

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

it('expone entradas internas de archivos como items virtuales', async () => {
  const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'fsScanner-test-'));
  const collectionFile = path.join(tmpDir, 'collection.zip');
  await fs.writeFile(collectionFile, '');

  (listArchiveEntries as unknown as Mock).mockResolvedValue([
    { name: 'inner/rom1.bin', size: 1234 },
  ]);

  const results = await scanDirectory(tmpDir);

  const expectedPath = `${collectionFile}::inner/rom1.bin`;
  const found = results.find((r: any) => r.path === expectedPath);

  expect(found).toBeDefined();
  expect(found.isArchiveEntry).toBe(true);
  expect(found.containerPath).toBe(collectionFile);
  expect(found.entryPath).toBe('inner/rom1.bin');
  expect(found.filename).toBe('rom1.bin');
  expect(found.format).toBe('bin');

  await fs.rm(tmpDir, { recursive: true, force: true });
});

it('ignora entradas con traversal o paths absolutos', async () => {
  const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'fsScanner-test-'));
  const collectionFile = path.join(tmpDir, 'collection.zip');
  await fs.writeFile(collectionFile, '');

  (listArchiveEntries as unknown as Mock).mockResolvedValue([
    { name: '../evil.rom', size: 10 },
    { name: '/abs/evil.rom', size: 20 },
    { name: 'good/rom.bin', size: 30 },
  ]);

  const results = await scanDirectory(tmpDir);
  const safePath = `${collectionFile}::good/rom.bin`;
  expect(results.find((r: any) => r.path === safePath)).toBeDefined();
  expect(results.find((r: any) => r.path === `${collectionFile}::../evil.rom`)).toBeUndefined();
  expect(results.find((r: any) => r.path === `${collectionFile}::/abs/evil.rom`)).toBeUndefined();

  await fs.rm(tmpDir, { recursive: true, force: true });
});

it('respeta ARCHIVE_MAX_ENTRIES', async () => {
  const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'fsScanner-test-'));
  const collectionFile = path.join(tmpDir, 'collection.zip');
  await fs.writeFile(collectionFile, '');

  // Set env var temporarily
  const prev = process.env.ARCHIVE_MAX_ENTRIES;
  process.env.ARCHIVE_MAX_ENTRIES = '1';

  (listArchiveEntries as unknown as Mock).mockResolvedValue([
    { name: 'one.bin', size: 1 },
    { name: 'two.bin', size: 2 },
    { name: 'three.bin', size: 3 },
  ]);

  const results = await scanDirectory(tmpDir);
  const matches = results.filter((r: any) => String(r.path).startsWith(collectionFile + '::'));
  expect(matches.length).toBe(1);

  // restore
  if (prev === undefined) delete process.env.ARCHIVE_MAX_ENTRIES;
  else process.env.ARCHIVE_MAX_ENTRIES = prev;

  await fs.rm(tmpDir, { recursive: true, force: true });
});
