<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\AgentSecurityScan;
use App\Services\AuditLogger;
use App\Services\Security\SecurityScanEvaluator;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;

/**
 * Agent-facing security scan ingest. One POST per completed scan, with
 * the raw findings array inline. We trust the agent to identify the
 * *category* of each finding (it runs the pattern matchers) but the
 * *severity* is decided here in SecurityScanEvaluator — agents must
 * not be able to under-report severity.
 *
 * Auth: agent.auth middleware → $request->attributes->get('agent_server').
 * Cross-server replay protection: every persisted row uses $server->id
 * from the middleware, never a client-supplied server id.
 */
class AgentSecurityScanController extends Controller
{
    public function __construct(private SecurityScanEvaluator $evaluator) {}

    public function store(Request $request): JsonResponse
    {
        $server = $request->attributes->get('agent_server');

        $data = $request->validate([
            'trigger'        => ['nullable', 'string', 'in:scheduled,manual,incident'],
            'started_at'     => ['nullable', 'date'],
            'agent_version'  => ['nullable', 'string', 'max:32'],
            'host_summary'   => ['nullable', 'array'],

            // Findings: array of detections. Each carries the agent's
            // category + raw evidence. Severity is NOT taken from here
            // — the evaluator scores it server-side.
            'findings'                          => ['present', 'array'],
            'findings.*.category'               => ['required', 'string', 'max:32'],
            'findings.*.unix_user'              => ['nullable', 'string', 'max:64'],
            'findings.*.file_path'              => ['nullable', 'string', 'max:512'],
            'findings.*.file_hash_sha256'       => ['nullable', 'string', 'size:64'],
            'findings.*.file_size_bytes'        => ['nullable', 'integer', 'min:0'],
            'findings.*.file_permissions'       => ['nullable', 'string', 'max:8'],
            'findings.*.process_pid'            => ['nullable', 'integer', 'min:0'],
            'findings.*.process_name'           => ['nullable', 'string', 'max:128'],
            'findings.*.process_cmdline'        => ['nullable', 'string', 'max:8192'],
            'findings.*.process_cpu_percent'    => ['nullable', 'numeric', 'min:0'],
            'findings.*.process_rss_kb'         => ['nullable', 'integer', 'min:0'],
            'findings.*.remote_ip'              => ['nullable', 'string', 'max:45'],
            'findings.*.remote_port'            => ['nullable', 'integer', 'min:0', 'max:65535'],
            'findings.*.evidence'               => ['nullable', 'array'],
        ]);

        $scan = AgentSecurityScan::create([
            'server_id'     => $server->id,
            'trigger'       => $data['trigger']      ?? 'scheduled',
            'started_at'    => $data['started_at']   ?? now(),
            'completed_at'  => now(),
            'agent_version' => $data['agent_version']?? null,
            'host_summary'  => $data['host_summary'] ?? null,
        ]);

        $persisted = $this->evaluator->process($scan, $data['findings']);

        // Refresh to read back the tally counters the evaluator wrote.
        $scan->refresh();

        AuditLogger::log('system', 'security-scan', 'ingested',
            'security_scan.ingested', 'agent_security_scan', (string) $scan->id,
            [
                'server_id'         => $server->id,
                'findings_total'    => $scan->findings_total,
                'findings_critical' => $scan->findings_critical,
                'findings_high'     => $scan->findings_high,
                'persisted_new'     => $persisted,
                'trigger'           => $scan->trigger,
            ]);

        return response()->json([
            'ok'                 => true,
            'scan_id'            => $scan->id,
            'findings_total'     => (int) $scan->findings_total,
            'findings_critical'  => (int) $scan->findings_critical,
            'findings_high'      => (int) $scan->findings_high,
            'findings_medium'    => (int) $scan->findings_medium,
            'persisted_new'      => $persisted,
        ]);
    }
}
