'use strict';

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');

const execute = process.argv.includes('--execute-read-only');
const roots = [
  { serviceId: 'aisfor', root: 'E:\\aisfor\\uploads' },
  { serviceId: 'aistalk', root: 'F:\\aistalk\\uploads' },
  { serviceId: 'aisfor-group', root: 'E:\\aisfor-group\\uploads' },
  { serviceId: 'aismall', root: 'E:\\aismall\\uploads' },
  { serviceId: 'plai-sharing', root: 'F:\\plai-sharing\\uploads' }
];

function hashFile(filePath) {
  return new Promise((resolve, reject) => {
    const hash = crypto.createHash('sha256');
    const stream = fs.createReadStream(filePath);
    stream.on('data', chunk => hash.update(chunk));
    stream.once('error', reject);
    stream.once('end', () => resolve(hash.digest('hex')));
  });
}

function enumerate(root) {
  const files = [];
  const stack = [root];
  while (stack.length) {
    const current = stack.pop();
    for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
      const fullPath = path.join(current, entry.name);
      if (entry.isSymbolicLink()) throw new Error('MEDIA_INVENTORY_SYMBOLIC_LINK_REJECTED');
      if (entry.isDirectory()) stack.push(fullPath);
      else if (entry.isFile()) files.push(fullPath);
    }
  }
  return files.sort((a, b) => a.localeCompare(b, 'en'));
}

async function inventoryService(service) {
  const root = path.resolve(service.root);
  if (!fs.existsSync(root)) return { serviceId: service.serviceId, present: false, files: 0, bytes: 0, contentRoot: null };
  const leaves = [];
  let bytes = 0;
  for (const filePath of enumerate(root)) {
    const resolved = path.resolve(filePath);
    if (!resolved.toLowerCase().startsWith(`${root}${path.sep}`.toLowerCase())) throw new Error('MEDIA_INVENTORY_PATH_ESCAPE_REJECTED');
    const before = fs.statSync(resolved);
    const contentHash = await hashFile(resolved);
    const after = fs.statSync(resolved);
    if (before.size !== after.size || before.mtimeMs !== after.mtimeMs) throw new Error('MEDIA_INVENTORY_FILE_CHANGED_DURING_READ');
    const relative = path.relative(root, resolved).replace(/\\/g, '/');
    leaves.push(crypto.createHash('sha256').update(`${service.serviceId}\0${relative}\0${after.size}\0${contentHash}`).digest('hex'));
    bytes += after.size;
  }
  return {
    serviceId: service.serviceId,
    present: true,
    files: leaves.length,
    bytes,
    contentRoot: crypto.createHash('sha256').update(leaves.join('\n')).digest('hex')
  };
}

(async () => {
  console.log(JSON.stringify({
    ok: true,
    mode: execute ? 'EXECUTE_READ_ONLY_CONTENT_INVENTORY' : 'PLAN_ONLY_NO_FILE_CONTENT_READ',
    serviceCount: roots.length,
    outputIncludesPaths: false,
    outputIncludesContent: false,
    filesWritten: false,
    externalTransfersMade: false,
    productionDatabaseAccessed: false
  }, null, 2));
  if (!execute) return;
  const services = [];
  for (const service of roots) services.push(await inventoryService(service));
  const aggregate = crypto.createHash('sha256')
    .update(services.map(row => `${row.serviceId}\0${row.files}\0${row.bytes}\0${row.contentRoot}`).join('\n'))
    .digest('hex');
  console.log(JSON.stringify({
    ok: true,
    status: 'FIVE_SERVICE_MEDIA_READ_ONLY_CONTENT_INVENTORY_COMPLETE',
    services,
    totalFiles: services.reduce((sum, row) => sum + row.files, 0),
    totalBytes: services.reduce((sum, row) => sum + row.bytes, 0),
    aggregateContentRoot: aggregate,
    outputRedacted: true,
    filesWritten: false,
    externalTransfersMade: false,
    productionChanged: false
  }, null, 2));
})().catch(error => {
  console.error(JSON.stringify({ ok: false, error: String(error.message || error) }, null, 2));
  process.exit(1);
});
