import { describe, it, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import { join } from 'node:path';
import { writeFile, readFile, mkdir, rm, stat } from 'node:fs/promises';
import { mkdirSync, writeFileSync } from 'node:fs';
import { tmpdir, homedir } from 'node:os';
import { createBackup, getBackupDir, checksum } from '../../scanners/lib/backup.mjs';
import { listBackups, restoreBackup, deleteBackup } from '../../scanners/rollback-engine.mjs';

/** Create a temp file and back it up, returning paths and content. */
async function setupTestBackup() {
  const tmpDir = join(tmpdir(), `config-audit-rb-test-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
  mkdirSync(tmpDir, { recursive: true });

  const testFile = join(tmpDir, 'test-settings.json');
  const originalContent = '{"original": true, "key": "value"}';
  writeFileSync(testFile, originalContent);

  const backup = createBackup([testFile]);

  // Now modify the file to simulate a change
  writeFileSync(testFile, '{"modified": true}');

  return { tmpDir, testFile, originalContent, backup };
}

describe('listBackups', () => {
  it('returns an array of backups', async () => {
    const result = await listBackups();
    assert.ok(Array.isArray(result.backups), 'Should return backups array');
  });

  it('backups are sorted newest first', async () => {
    const result = await listBackups();
    if (result.backups.length >= 2) {
      assert.ok(result.backups[0].id >= result.backups[1].id, 'First backup should be newer');
    }
  });

  it('each backup has required fields', async () => {
    const { tmpDir, backup } = await setupTestBackup();
    try {
      const result = await listBackups();
      const found = result.backups.find(b => b.id === backup.backupId);
      assert.ok(found, 'Should find our test backup');
      assert.ok(found.id, 'Backup should have id');
      assert.ok(found.createdAt, 'Backup should have createdAt');
      assert.ok(Array.isArray(found.files), 'Backup should have files array');
      assert.ok(found.files.length > 0, 'Backup should have at least one file');
      assert.ok(found.files[0].originalPath, 'File entry should have originalPath');
      assert.ok(found.files[0].checksum, 'File entry should have checksum');
    } finally {
      await rm(tmpDir, { recursive: true, force: true });
    }
  });
});

describe('restoreBackup', () => {
  let tmpDir, testFile, originalContent, backup;

  beforeEach(async () => {
    ({ tmpDir, testFile, originalContent, backup } = await setupTestBackup());
  });

  afterEach(async () => {
    if (tmpDir) await rm(tmpDir, { recursive: true, force: true });
    // Cleanup our test backup
    try { await deleteBackup(backup.backupId); } catch {}
  });

  it('restores files to original content', async () => {
    const result = await restoreBackup(backup.backupId);
    assert.ok(result.restored.length > 0, 'Should restore at least one file');
    assert.strictEqual(result.failed.length, 0, 'No failures');

    const restoredContent = await readFile(testFile, 'utf-8');
    assert.strictEqual(restoredContent, originalContent, 'Content should match original');
  });

  it('verifies checksums after restore', async () => {
    const result = await restoreBackup(backup.backupId, { verify: true });
    for (const r of result.restored) {
      assert.strictEqual(r.status, 'restored');
    }
  });

  it('dry-run returns plan without writing', async () => {
    const result = await restoreBackup(backup.backupId, { dryRun: true });
    assert.ok(result.restored.length > 0);
    for (const r of result.restored) {
      assert.strictEqual(r.status, 'dry-run');
    }

    // File should still be modified
    const content = await readFile(testFile, 'utf-8');
    assert.strictEqual(content, '{"modified": true}', 'File should not be restored in dry-run');
  });

  it('throws for invalid backup-id', async () => {
    await assert.rejects(
      () => restoreBackup('nonexistent_99999999_999999'),
      { message: /Backup not found/ },
    );
  });
});

describe('deleteBackup', () => {
  it('deletes an existing backup', async () => {
    const { tmpDir, backup } = await setupTestBackup();
    try {
      const result = await deleteBackup(backup.backupId);
      assert.strictEqual(result.deleted, true);

      // Verify it's gone from the list
      const list = await listBackups();
      const found = list.backups.find(b => b.id === backup.backupId);
      assert.ok(!found, 'Deleted backup should not appear in list');
    } finally {
      await rm(tmpDir, { recursive: true, force: true });
    }
  });

  it('returns error for nonexistent backup', async () => {
    const result = await deleteBackup('nonexistent_99999999_999999');
    assert.strictEqual(result.deleted, false);
    assert.ok(result.error, 'Should have error message');
  });
});
