'use strict';

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

const root = fs.mkdtempSync(path.join(os.tmpdir(), 'plai-media-replica-'));
const nodes = [
  { id: 'node-a', domain: 'wan-a' },
  { id: 'node-b', domain: 'wan-b' },
  { id: 'node-c', domain: 'wan-c' },
  { id: 'node-d', domain: 'wan-d' }
];
const sha256 = value => crypto.createHash('sha256').update(value).digest('hex');

function encrypt(plaintext, key, objectId) {
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
  cipher.setAAD(Buffer.from(objectId));
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
  return Buffer.concat([iv, cipher.getAuthTag(), ciphertext]);
}

function decrypt(envelope, key, objectId) {
  const decipher = crypto.createDecipheriv('aes-256-gcm', key, envelope.subarray(0, 12));
  decipher.setAuthTag(envelope.subarray(12, 28));
  decipher.setAAD(Buffer.from(objectId));
  return Buffer.concat([decipher.update(envelope.subarray(28)), decipher.final()]);
}

function safeCleanup() {
  const resolved = path.resolve(root);
  const base = `${path.resolve(os.tmpdir())}${path.sep}`.toLowerCase();
  if (!resolved.toLowerCase().startsWith(base) || !path.basename(resolved).startsWith('plai-media-replica-')) {
    throw new Error('MEDIA_REPLICA_TEMP_CLEANUP_TARGET_REJECTED');
  }
  fs.rmSync(resolved, { recursive: true, force: true });
}

try {
  for (const node of nodes) fs.mkdirSync(path.join(root, node.id));
  const plaintext = crypto.randomBytes(2 * 1024 * 1024 + 137);
  const plaintextHash = sha256(plaintext);
  const objectId = `synthetic-media:${plaintextHash}`;
  const key = crypto.randomBytes(32);
  const envelope = encrypt(plaintext, key, objectId);
  const ciphertextHash = sha256(envelope);
  const selected = nodes.slice(0, 3);
  assert.equal(new Set(selected.map(node => node.domain)).size, 3);

  for (const node of selected) fs.writeFileSync(path.join(root, node.id, ciphertextHash), envelope);
  for (const node of selected) {
    const stored = fs.readFileSync(path.join(root, node.id, ciphertextHash));
    assert.equal(sha256(stored), ciphertextHash);
    assert.notEqual(sha256(stored), plaintextHash);
  }

  fs.unlinkSync(path.join(root, 'node-b', ciphertextHash));
  const repairSource = fs.readFileSync(path.join(root, 'node-a', ciphertextHash));
  assert.equal(sha256(repairSource), ciphertextHash);
  fs.writeFileSync(path.join(root, 'node-d', ciphertextHash), repairSource);
  assert.equal(sha256(fs.readFileSync(path.join(root, 'node-d', ciphertextHash))), ciphertextHash);

  const restored = decrypt(fs.readFileSync(path.join(root, 'node-c', ciphertextHash)), key, objectId);
  assert.equal(sha256(restored), plaintextHash);
  const tampered = Buffer.from(repairSource);
  tampered[tampered.length - 1] ^= 1;
  assert.throws(() => decrypt(tampered, key, objectId));

  safeCleanup();
  assert.equal(fs.existsSync(root), false);
  console.log(JSON.stringify({
    ok: true,
    status: 'SYNTHETIC_SERVICE_MEDIA_THREE_DOMAIN_REPAIR_RESTORE_PROVEN_AND_CLEANED',
    algorithm: 'aes-256-gcm',
    plaintextBytes: plaintext.length,
    initialCiphertextReplicas: 3,
    initialFailureDomains: 3,
    oneReplicaLossSimulated: true,
    ciphertextOnlyRepairToFourthDomain: true,
    restoredPlaintextHashVerified: true,
    tamperedCiphertextRejected: true,
    storageNodesReceivedDecryptionKey: false,
    temporaryDataDeleted: true,
    productionChanged: false
  }, null, 2));
} catch (error) {
  try { safeCleanup(); } catch (_) {}
  throw error;
}
