const { spawnSync } = require('child_process');

const ENTROPY_LABEL = 'PLAIS_OPERATOR_CUSTODY_V1';

function runPowerShell(command, args = [], input = '') {
  const result = spawnSync('powershell.exe', [
    '-NoLogo',
    '-NoProfile',
    '-NonInteractive',
    '-ExecutionPolicy', 'Bypass',
    '-Command', command,
    ...args
  ], {
    input,
    encoding: 'utf8',
    windowsHide: true,
    maxBuffer: 1024 * 1024
  });
  if (result.status !== 0) {
    const diagnostic = String(result.stderr || '').replace(/[\r\n]+/g, ' ').trim().slice(0, 800);
    const error = new Error(`PowerShell custody operation failed with exit code ${result.status}${diagnostic ? `: ${diagnostic}` : '.'}`);
    error.code = 'OPERATOR_CUSTODY_POWERSHELL_FAILED';
    throw error;
  }
  return String(result.stdout || '').trim();
}

function protect(plaintext) {
  const command = [
    'Add-Type -AssemblyName System.Security',
    '$value = [Console]::In.ReadToEnd().Trim()',
    '$bytes = [Text.Encoding]::UTF8.GetBytes($value)',
    `$entropy = [Text.Encoding]::UTF8.GetBytes('${ENTROPY_LABEL}')`,
    '$cipher = [Security.Cryptography.ProtectedData]::Protect($bytes, $entropy, [Security.Cryptography.DataProtectionScope]::CurrentUser)',
    '[Convert]::ToBase64String($cipher)'
  ].join('; ');
  return runPowerShell(command, [], plaintext);
}

function unprotect(ciphertext) {
  const command = [
    'Add-Type -AssemblyName System.Security',
    '$value = [Console]::In.ReadToEnd().Trim()',
    '$cipher = [Convert]::FromBase64String($value)',
    `$entropy = [Text.Encoding]::UTF8.GetBytes('${ENTROPY_LABEL}')`,
    '$bytes = [Security.Cryptography.ProtectedData]::Unprotect($cipher, $entropy, [Security.Cryptography.DataProtectionScope]::CurrentUser)',
    '[Text.Encoding]::UTF8.GetString($bytes)'
  ].join('; ');
  return runPowerShell(command, [], ciphertext);
}

function restrictAcl(targetPath, isDirectory) {
  try {
    const current = auditAcl(targetPath);
    if (current.ok) return current;
  } catch (error) {
    // A new or inherited target is normalized below.
  }
  const command = [
    '$payload = [Console]::In.ReadToEnd() | ConvertFrom-Json',
    '$target = [string]$payload.target',
    '$isDirectory = [bool]$payload.isDirectory',
    '$currentSid = [Security.Principal.WindowsIdentity]::GetCurrent().User',
    '$required = @($currentSid, [Security.Principal.SecurityIdentifier]::new("S-1-5-18"), [Security.Principal.SecurityIdentifier]::new("S-1-5-32-544"))',
    '$acl = Get-Acl -LiteralPath $target',
    '$acl.SetAccessRuleProtection($true, $false)',
    'foreach ($existing in @($acl.Access)) { [void]$acl.RemoveAccessRuleSpecific($existing) }',
    '$inheritance = [Security.AccessControl.InheritanceFlags]::None',
    'if ($isDirectory) { $inheritance = [Security.AccessControl.InheritanceFlags]::ContainerInherit -bor [Security.AccessControl.InheritanceFlags]::ObjectInherit }',
    'foreach ($sid in $required) { $rule = [Security.AccessControl.FileSystemAccessRule]::new($sid, [Security.AccessControl.FileSystemRights]::FullControl, $inheritance, [Security.AccessControl.PropagationFlags]::None, [Security.AccessControl.AccessControlType]::Allow); [void]$acl.AddAccessRule($rule) }',
    'Set-Acl -LiteralPath $target -AclObject $acl'
  ].join('; ');
  runPowerShell(command, [], JSON.stringify({ target: targetPath, isDirectory: Boolean(isDirectory) }));
  return auditAcl(targetPath);
}

function auditAcl(targetPath) {
  const command = [
    '$payload = [Console]::In.ReadToEnd() | ConvertFrom-Json',
    '$target = [string]$payload.target',
    '$currentSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value',
    '$required = @($currentSid, "S-1-5-18", "S-1-5-32-544")',
    '$acl = Get-Acl -LiteralPath $target',
    '$allowed = @($acl.Access | Where-Object { $_.AccessControlType -eq [Security.AccessControl.AccessControlType]::Allow } | ForEach-Object { try { $_.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value } catch { $_.IdentityReference.Value } } | Sort-Object -Unique)',
    '$unexpected = @($allowed | Where-Object { $required -notcontains $_ })',
    '$missing = @($required | Where-Object { $allowed -notcontains $_ })',
    '$result = [ordered]@{ protected = [bool]$acl.AreAccessRulesProtected; requiredPresent = ($missing.Count -eq 0); unexpectedAllowRules = ($unexpected.Count -gt 0); ruleCount = $allowed.Count }',
    '$result | ConvertTo-Json -Compress'
  ].join('; ');
  const parsed = JSON.parse(runPowerShell(command, [], JSON.stringify({ target: targetPath })));
  return {
    protected: parsed.protected === true,
    requiredPresent: parsed.requiredPresent === true,
    unexpectedAllowRules: parsed.unexpectedAllowRules === true,
    ruleCount: Number(parsed.ruleCount || 0),
    ok: parsed.protected === true && parsed.requiredPresent === true && parsed.unexpectedAllowRules !== true
  };
}

module.exports = { auditAcl, protect, restrictAcl, unprotect };
