/**
 * Backup library for config-audit.
 * Creates timestamped backups of config files with checksums and manifests.
 * Zero external dependencies.
 */

import { readFileSync, writeFileSync, copyFileSync, mkdirSync, readdirSync, existsSync, statSync, rmSync, readFile } from 'node:fs';
import { readFile as readFileAsync } from 'node:fs/promises';
import { join, basename } from 'node:path';
import { createHash } from 'node:crypto';
import { homedir } from 'node:os';

const BACKUP_ROOT = join(homedir(), '.config-audit', 'backups');
const MAX_BACKUPS = 10;

/**
 * Get the backup root directory path.
 * @returns {string}
 */
export function getBackupDir() {
  return BACKUP_ROOT;
}

/**
 * Generate a timestamp-based backup ID.
 * @returns {string} Format: YYYYMMDD_HHMMSS
 */
export function generateBackupId() {
  const now = new Date();
  const y = now.getFullYear();
  const m = String(now.getMonth() + 1).padStart(2, '0');
  const d = String(now.getDate()).padStart(2, '0');
  const h = String(now.getHours()).padStart(2, '0');
  const min = String(now.getMinutes()).padStart(2, '0');
  const s = String(now.getSeconds()).padStart(2, '0');
  return `${y}${m}${d}_${h}${min}${s}`;
}

/**
 * Create a safe filename from a file path (replace path separators with _).
 * @param {string} filePath
 * @returns {string}
 */
export function safeFileName(filePath) {
  return filePath.replace(/[\\\/]/g, '_');
}

/**
 * Calculate SHA-256 checksum of a buffer or string.
 * @param {Buffer|string} content
 * @returns {string}
 */
export function checksum(content) {
  return createHash('sha256').update(content).digest('hex');
}

/**
 * Create a backup of the specified files.
 * @param {string[]} files - Array of absolute file paths to back up
 * @param {object} [opts]
 * @param {string} [opts.backupId] - Override backup ID (for testing)
 * @returns {{ backupId: string, backupPath: string, manifest: object }}
 */
export function createBackup(files, opts = {}) {
  const backupId = opts.backupId || generateBackupId();
  const backupPath = join(BACKUP_ROOT, backupId);
  const filesDir = join(backupPath, 'files');

  mkdirSync(filesDir, { recursive: true });

  const manifestFiles = [];

  for (const file of files) {
    if (!existsSync(file)) continue;

    const safeName = safeFileName(file);
    copyFileSync(file, join(filesDir, safeName));

    const content = readFileSync(file);
    const hash = checksum(content);
    const sizeBytes = statSync(file).size;

    manifestFiles.push({
      originalPath: file,
      backupPath: `./files/${safeName}`,
      checksum: hash,
      sizeBytes,
    });
  }

  const manifest = {
    created_at: new Date().toISOString(),
    backup_id: backupId,
    files: manifestFiles,
  };

  // Write manifest as YAML-like format
  const manifestYaml = serializeManifest(manifest);
  writeFileSync(join(backupPath, 'manifest.yaml'), manifestYaml);

  // Cleanup old backups
  cleanupOldBackups();

  return { backupId, backupPath, manifest };
}

/**
 * Serialize manifest to YAML-like format.
 * @param {object} manifest
 * @returns {string}
 */
function serializeManifest(manifest) {
  let yaml = `created_at: "${manifest.created_at}"\n`;
  yaml += `backup_id: "${manifest.backup_id}"\n`;
  yaml += `files:\n`;
  for (const f of manifest.files) {
    yaml += `  - original_path: "${f.originalPath}"\n`;
    yaml += `    backup_path: "${f.backupPath}"\n`;
    yaml += `    checksum: "${f.checksum}"\n`;
    yaml += `    size_bytes: ${f.sizeBytes}\n`;
  }
  return yaml;
}

/**
 * Parse a manifest.yaml file content.
 * @param {string} content
 * @returns {object}
 */
export function parseManifest(content) {
  const result = { created_at: '', backup_id: '', files: [] };

  const createdMatch = content.match(/created_at:\s*"([^"]+)"/);
  if (createdMatch) result.created_at = createdMatch[1];

  const idMatch = content.match(/backup_id:\s*"([^"]+)"/);
  if (idMatch) result.backup_id = idMatch[1];

  // Parse file entries
  const fileBlocks = content.split(/\n\s+-\s+original_path:/).slice(1);
  for (const block of fileBlocks) {
    const origMatch = block.match(/^\s*"([^"]+)"/);
    const bpMatch = block.match(/backup_path:\s*"([^"]+)"/);
    const csMatch = block.match(/checksum:\s*"([^"]+)"/);
    const szMatch = block.match(/size_bytes:\s*(\d+)/);

    if (origMatch && bpMatch && csMatch) {
      result.files.push({
        originalPath: origMatch[1],
        backupPath: bpMatch[1],
        checksum: csMatch[1],
        sizeBytes: szMatch ? parseInt(szMatch[1], 10) : 0,
      });
    }
  }

  return result;
}

/**
 * Remove old backups beyond MAX_BACKUPS.
 */
function cleanupOldBackups() {
  if (!existsSync(BACKUP_ROOT)) return;

  const dirs = readdirSync(BACKUP_ROOT, { withFileTypes: true })
    .filter(d => d.isDirectory())
    .map(d => d.name)
    .sort();

  if (dirs.length > MAX_BACKUPS) {
    const toDelete = dirs.slice(0, dirs.length - MAX_BACKUPS);
    for (const dir of toDelete) {
      rmSync(join(BACKUP_ROOT, dir), { recursive: true, force: true });
    }
  }
}

export { MAX_BACKUPS };
