<?php
declare(strict_types=1);
define('DASHBOARD_ROOT', dirname(__DIR__, 2));
require_once DASHBOARD_ROOT . '/core/bootstrap.php';
header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit(json_encode(['ok'=>false,'error'=>'POST required']));
}

RBAC::enforce('view.fleet');
CSRF::enforce();

$action  = $_POST['action'] ?? '';
$nodeKey = $_POST['node'] ?? '';
$service = $_POST['service'] ?? '';

if (!$action || !$nodeKey) {
    echo json_encode(['ok'=>false,'error'=>'Missing action or node']);
    exit;
}

require_once DASHBOARD_ROOT . '/core/fleet_orchestrator.php';

$node = Database::fetchOne(
    'SELECT id, node_id, node_role, hostname, wg_ip, public_ip, ssh_port, status FROM nodes WHERE node_id = :nid',
    [':nid' => $nodeKey]
);
if (!$node) {
    echo json_encode(['ok'=>false,'error'=>'Node not found: '.$nodeKey]);
    exit;
}

$dbId    = (int)$node['id'];
$wgIp    = $node['wg_ip'] ?? '';
$sshPort = (int)($node['ssh_port'] ?: 33022);
$role    = $node['node_role'] ?? '';

try { $wgIp = Encryption::decrypt($wgIp) ?: $wgIp; } catch (\Throwable $e) {}

$t0 = hrtime(true);
$result = ['ok'=>true,'output'=>'','elapsed'=>'','route'=>'local'];

try {
    switch ($action) {
        case 'health_check':
            $uptime = runNodeCmd($wgIp, $sshPort, 'uptime');
            $load   = runNodeCmd($wgIp, $sshPort, 'cat /proc/loadavg');
            $mem    = runNodeCmd($wgIp, $sshPort, 'free -h | head -2');
            $disk   = runNodeCmd($wgIp, $sshPort, 'df -h / | tail -1');
            $result['output'] = "=== UPTIME ===\n" . $uptime .
                                "\n\n=== LOAD ===\n" . $load .
                                "\n\n=== MEMORY ===\n" . $mem .
                                "\n\n=== DISK ===\n" . $disk;
            break;

        case 'service_status':
            if (!$service) { $result = ['ok'=>false,'error'=>'No service specified']; break; }
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'systemctl status ' . escapeshellarg($service) . ' --no-pager -l 2>&1 | head -20');
            break;

        case 'service_restart':
            RBAC::enforce('fleet.command');
            if (!$service) { $result = ['ok'=>false,'error'=>'No service specified']; break; }
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo systemctl restart ' . escapeshellarg($service) . ' 2>&1 && echo OK');
            break;

        case 'service_stop':
            RBAC::enforce('fleet.command');
            if (!$service) { $result = ['ok'=>false,'error'=>'No service specified']; break; }
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo systemctl stop ' . escapeshellarg($service) . ' 2>&1 && echo OK');
            break;

        case 'service_start':
            RBAC::enforce('fleet.command');
            if (!$service) { $result = ['ok'=>false,'error'=>'No service specified']; break; }
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo systemctl start ' . escapeshellarg($service) . ' 2>&1 && echo OK');
            break;

        case 'logs_tail':
            if (!$service) { $result = ['ok'=>false,'error'=>'No service specified']; break; }
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo journalctl -u ' . escapeshellarg($service) . ' --no-pager -n 30 2>&1');
            break;

        case 'network_test':
            $ping = runNodeCmd($wgIp, $sshPort, 'ping -c 3 1.1.1.1 2>&1');
            $dns  = runNodeCmd($wgIp, $sshPort, 'dig +short google.com 2>&1');
            $wg   = runNodeCmd($wgIp, $sshPort, 'sudo wg show 2>&1 | head -15');
            $result['output'] = "=== PING ===\n" . $ping . "\n\n=== DNS ===\n" . $dns . "\n\n=== WIREGUARD ===\n" . $wg;
            break;

        case 'wg_status':
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo wg show 2>&1');
            break;

        case 'firewall_status':
            $fw = runNodeCmd($wgIp, $sshPort, 'sudo firewall-cmd --list-all 2>/dev/null || sudo iptables -L -n 2>&1 | head -30');
            $result['output'] = $fw;
            break;

        case 'update_check':
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo dnf check-update 2>&1 | head -30');
            break;

        case 'processes':
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'ps aux --sort=-%cpu | head -20');
            break;

        case 'uptime':
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'uptime && echo && cat /proc/loadavg');
            break;

        case 'ssh_status':
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo firewall-cmd --query-port=33022/tcp 2>/dev/null && echo OPEN || echo CLOSED');
            break;

        case 'ssh_open':
            RBAC::enforce('fleet.command');
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo firewall-cmd --add-port=33022/tcp --timeout=1800 2>&1 && echo SSH opened for 30 minutes');
            break;

        case 'ssh_close':
            RBAC::enforce('fleet.command');
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo firewall-cmd --remove-port=33022/tcp 2>&1 && echo SSH port closed');
            break;

        case 'kernel_harden':
            RBAC::enforce('fleet.command');
            $result['output'] = runNodeCmd($wgIp, $sshPort, 'sudo sysctl -p /etc/sysctl.d/99-hardening.conf 2>&1 || echo No hardening config found');
            break;

        default:
            $result = ['ok'=>false,'error'=>'Unknown action: '.$action];
    }
} catch (\Throwable $e) {
    $result = ['ok'=>false,'error'=>$e->getMessage(),'output'=>'Command failed'];
}

$t1 = hrtime(true);
$result['elapsed'] = round(($t1 - $t0) / 1e9, 2) . 's';
$result['route'] = 'ssh://' . $wgIp . ':' . $sshPort;

echo json_encode($result);

function runNodeCmd(string $ip, int $port, string $cmd): string {
    if (!$ip) return 'ERROR: No IP configured for node';
    $keyFile = '/var/www/private/fleet.key';
    if (!file_exists($keyFile)) {
        $keyFile = '/home/admin/.ssh/id_ed25519';
        if (!file_exists($keyFile)) return 'ERROR: No SSH key available';
    }
    $sshCmd = sprintf(
        'ssh -i %s -p %d -o StrictHostKeyChecking=no -o ConnectTimeout=8 -o BatchMode=yes admin@%s %s 2>&1',
        escapeshellarg($keyFile),
        $port,
        escapeshellarg($ip),
        escapeshellarg($cmd)
    );
    $output = [];
    $code = 0;
    exec($sshCmd, $output, $code);
    return implode("\n", $output);
}