<?php

namespace App\Services\Audit;

/**
 * Deterministic finding extraction from parsed audit sections. No LLM
 * involved — pattern match the script's section markers and emit a finding
 * record per detected condition.
 *
 * Severity ladder (fixed):
 *   - SSH brute force:    >1000 attempts/24h = critical, >200 = high
 *   - Pending reboot:     high (kernel/firmware not loaded)
 *   - Public DB / rpcbind: high
 *   - Service in failed state: medium
 *   - Disk usage:         95%+ critical, 90%+ high, 85%+ medium
 *   - Kernel CVE backlog: high if >=5 advisories, medium otherwise
 */
class HeuristicFindings
{
    /**
     * @return list<array{severity:string,category:string,cve_id:string,title:string,description:string,
     *                    fixable:bool,fix_command?:?string,rollback_command?:?string,risk?:string,reasoning?:string}>
     */
    public function extract(array $sections): array
    {
        $f = [];

        // SSH brute force + sshd posture
        if (isset($sections['J_FAILED_LOGINS']) && isset($sections['J_SSHD'])) {
            $failed = (int) trim(explode("\n", $sections['J_FAILED_LOGINS'])[0]);
            $sshd = $sections['J_SSHD'];
            $rootLogin = preg_match('/^permitrootlogin\s+(\w+)/im', $sshd, $m1) ? strtolower($m1[1]) : 'unknown';
            $passwordAuth = preg_match('/^passwordauthentication\s+(\w+)/im', $sshd, $m2) ? strtolower($m2[1]) : 'unknown';

            if ($failed > 200) {
                $f[] = [
                    'severity'   => $failed > 1000 ? 'critical' : 'high',
                    'category'   => 'firewall_additive',
                    'cve_id'     => 'ssh-bruteforce',
                    'title'      => "{$failed} failed SSH login attempts in last 24h",
                    'description'=> "PermitRootLogin={$rootLogin}, PasswordAuthentication={$passwordAuth}. fail2ban/CSF inactive (separate finding).",
                    'fixable'    => true,
                    'fix_command'=> 'dnf install -y fail2ban && systemctl enable --now fail2ban && fail2ban-client reload',
                    'rollback_command' => 'systemctl disable --now fail2ban',
                    'risk'       => 'medium',
                    'reasoning'  => 'Active brute-force attack. Enabling fail2ban with default sshd jail throttles attempts immediately.',
                ];
            }
            if ($rootLogin === 'yes' && $passwordAuth === 'yes') {
                $f[] = [
                    'severity'   => 'high',
                    'category'   => 'config_change',
                    'cve_id'     => 'sshd-passwordauth-root',
                    'title'      => 'SSH allows root login with password',
                    'description'=> 'sshd -T reports PermitRootLogin=yes and PasswordAuthentication=yes. Combined with active brute-force, every attempt is fully unconstrained.',
                    'fixable'    => true,
                    'fix_command'=> "sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config && sed -i 's/^PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config && sshd -t && systemctl reload sshd",
                    'rollback_command' => "sed -i 's/^PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config && sed -i 's/^PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && systemctl reload sshd",
                    'risk'       => 'high',
                    'reasoning'  => 'Operator must have an SSH key already enrolled. Verify before approving — this command will lock out anyone without a key.',
                ];
            }
        }

        // Firewall daemons inactive
        if (isset($sections['H_FIREWALL'])) {
            $fw = $sections['H_FIREWALL'];
            $fail2ban = preg_match('/FAIL2BAN=(\w+)/', $fw, $m) ? $m[1] : 'unknown';
            $lfd = preg_match('/LFD=(\w+)/', $fw, $m) ? $m[1] : 'unknown';
            if ($fail2ban !== 'active' && $lfd !== 'active') {
                $f[] = [
                    'severity'   => 'high',
                    'category'   => 'firewall_additive',
                    'cve_id'     => 'no-firewall-daemon',
                    'title'      => 'No login-failure daemon running (fail2ban + lfd both inactive)',
                    'description'=> "fail2ban={$fail2ban}, lfd={$lfd}. Server has zero automated brute-force defense.",
                    'fixable'    => true,
                    'fix_command'=> 'dnf install -y fail2ban && systemctl enable --now fail2ban',
                    'rollback_command' => 'systemctl disable --now fail2ban',
                    'risk'       => 'low',
                    'reasoning'  => 'Install + enable fail2ban with default jails. Reversible.',
                ];
            }
        }

        // Public listeners that should be loopback-only
        if (isset($sections['H_LISTEN'])) {
            foreach (explode("\n", $sections['H_LISTEN']) as $ln) {
                if (preg_match('/0\.0\.0\.0:3306\b.*mariadbd/', $ln)) {
                    $f[] = [
                        'severity' => 'high', 'category' => 'config_change', 'cve_id' => 'mariadb-public-3306',
                        'title' => 'MariaDB bound to 0.0.0.0:3306 (publicly exposed)',
                        'description' => 'mariadbd listening on all interfaces. Should be 127.0.0.1 unless remote DB access is genuinely required.',
                        'fixable' => true,
                        'fix_command' => "echo '[mysqld]' > /etc/my.cnf.d/zz-bindaddr.cnf && echo 'bind-address = 127.0.0.1' >> /etc/my.cnf.d/zz-bindaddr.cnf && systemctl restart mariadb",
                        'rollback_command' => 'rm -f /etc/my.cnf.d/zz-bindaddr.cnf && systemctl restart mariadb',
                        'risk' => 'medium',
                        'reasoning' => 'Restart triggers a 1-3s DB downtime. Verify no off-host clients before approving.',
                    ];
                }
                if (preg_match('/0\.0\.0\.0:111\b.*rpcbind/', $ln)) {
                    $f[] = [
                        'severity' => 'high', 'category' => 'firewall_additive', 'cve_id' => 'rpcbind-public-111',
                        'title' => 'rpcbind listening on 0.0.0.0:111 (DDoS reflection vector)',
                        'description' => 'No NFS/RPC services configured here, but rpcbind is up and public.',
                        'fixable' => true,
                        'fix_command' => 'systemctl disable --now rpcbind.socket rpcbind',
                        'rollback_command' => 'systemctl enable --now rpcbind.socket rpcbind',
                        'risk' => 'low', 'reasoning' => 'Disabling rpcbind has no effect when NFS isn\'t in use. Reversible.',
                    ];
                }
            }
        }

        // Reboot pending
        if (isset($sections['A_KERNEL_REBOOT']) && stripos($sections['A_KERNEL_REBOOT'], 'reboot is required') !== false) {
            $pkgs = [];
            foreach (explode("\n", $sections['A_KERNEL_REBOOT']) as $ln) {
                if (preg_match('/^\s*\*\s*(\S+)/', $ln, $m)) $pkgs[] = $m[1];
            }
            $f[] = [
                'severity' => 'high', 'category' => 'package_security_update',
                'cve_id' => 'reboot-pending',
                'title' => 'Reboot required to load updated ' . (count($pkgs) ? implode(', ', $pkgs) : 'core packages'),
                'description' => trim($sections['A_KERNEL_REBOOT']),
                'fixable' => true,
                'fix_command' => "shutdown -r +5 'AI Portal: scheduled reboot for kernel/firmware update'",
                'rollback_command' => 'shutdown -c',
                'risk' => 'critical',
                'reasoning' => 'Reboot is a separate approval per auditor protocol. Customer-impacting downtime ~60-120s.',
            ];
        }

        // Kernel CVE backlog
        if (isset($sections['A_KERNEL_CVE'])) {
            $cveCount = preg_match_all('/^(CVE-\d{4}-\d+|RHSA-\d+|ALSA-\d+)/m', $sections['A_KERNEL_CVE']);
            if ($cveCount > 0) {
                $f[] = [
                    'severity' => $cveCount >= 5 ? 'high' : 'medium',
                    'category' => 'package_security_update',
                    'cve_id'   => 'kernel-cve-backlog',
                    'title'    => "{$cveCount} unapplied kernel security advisories",
                    'description' => substr($sections['A_KERNEL_CVE'], 0, 1500),
                    'fixable'  => true,
                    'fix_command' => 'dnf update -y kernel kernel-core kernel-modules linux-firmware microcode_ctl',
                    'rollback_command' => 'dnf history undo last',
                    'risk' => 'medium',
                    'reasoning' => 'Kernel update applies but does not load until reboot. Pair with the reboot approval.',
                ];
            }
        }

        // spamd failed
        if (isset($sections['E_MAIL']) && preg_match('/^spamd=failed$/m', $sections['E_MAIL'])) {
            $f[] = [
                'severity' => 'medium', 'category' => 'service_restart',
                'cve_id'   => 'spamd-failed',
                'title'    => 'SpamAssassin daemon (spamd) is in failed state',
                'description' => 'systemctl reports spamd=failed. Likely repeatedly restart-looped by tailwatchd.',
                'fixable' => true,
                'fix_command' => 'systemctl restart spamd && sleep 2 && systemctl is-active spamd',
                'rollback_command' => null,
                'risk' => 'low', 'reasoning' => 'Single-service restart. If it fails again, manual triage needed.',
            ];
        }

        // Disk thresholds — duplicate per partition
        if (isset($sections['G_DISK'])) {
            foreach (explode("\n", $sections['G_DISK']) as $ln) {
                if (preg_match('/\s(\d+)%\s+(\S+)$/', $ln, $m) && (int) $m[1] >= 85) {
                    $sev = (int) $m[1] >= 95 ? 'critical' : ((int) $m[1] >= 90 ? 'high' : 'medium');
                    $f[] = [
                        'severity' => $sev, 'category' => 'disk_cleanup',
                        'cve_id'   => 'disk-' . str_replace('/', '-', $m[2]),
                        'title'    => "Disk usage {$m[1]}% on {$m[2]}",
                        'description' => $ln,
                        'fixable' => true,
                        'fix_command' => "find /var/log -type f -name '*.gz' -mtime +14 -delete && journalctl --vacuum-time=14d",
                        'rollback_command' => null,
                        'risk' => 'low', 'reasoning' => 'Conservative cleanup of old gzipped logs and journald trim.',
                    ];
                }
            }
        }

        return $f;
    }
}
