<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

// Agent-driven daily security scanner. Distinct table name (prefix
// `agent_`) so it doesn't collide with the pre-existing `security_scans`
// table (used by the old read-only audit tool).
//
// Real-incident driven: caught a cryptojacker hiding in /var/tmp/
// disguised as systemd-logind that cpGuard + Imunify360 both missed.
// The detection patterns the agent runs come from that walkthrough —
// see resources/agent/agent-security.sh.
return new class extends Migration {
    public function up(): void
    {
        Schema::create('agent_security_scans', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained('servers')->cascadeOnDelete();
            // 'scheduled' = daily cron, 'manual' = operator clicked,
            // 'incident' = follow-up to an existing alert.
            $t->string('trigger', 24)->default('scheduled');
            $t->timestamp('started_at')->useCurrent();
            $t->timestamp('completed_at')->nullable();
            $t->unsignedSmallInteger('findings_total')->default(0);
            $t->unsignedSmallInteger('findings_critical')->default(0);
            $t->unsignedSmallInteger('findings_high')->default(0);
            $t->unsignedSmallInteger('findings_medium')->default(0);
            // Top processes / CPU / load / outbound count at scan time
            // so the dashboard can show server context alongside findings.
            $t->json('host_summary')->nullable();
            $t->string('agent_version', 32)->nullable();
            $t->timestamps();

            $t->index(['server_id', 'started_at']);
        });

        // One row per detection. Multiple findings per scan are normal:
        // a single cryptojacker drops a binary AND a persistence cron
        // AND a .bashrc hook — three findings, one root cause.
        Schema::create('agent_security_findings', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained('servers')->cascadeOnDelete();
            $t->foreignId('scan_id')->constrained('agent_security_scans')->cascadeOnDelete();
            // Detection categories — each maps to a specific agent
            // detection rule. Keep this list narrow.
            //   cryptojack_process — high-CPU process matching miner heuristics
            //   suspicious_binary  — executable in unusual location (/var/tmp, /dev/shm, .config/cron)
            //   persistence_cron   — user crontab pointing at user-home binary
            //   persistence_shell  — .bashrc / .profile installing executable hook
            //   outbound_mining    — TCP connection to known mining-pool port
            //   masqueraded_process— process name mimics system service (kworker-foo, cpu-logind)
            $t->string('category', 32)->index();
            $t->string('severity', 12)->default('medium')->index();
            $t->string('unix_user', 64)->nullable()->index();
            // File-system evidence
            $t->string('file_path', 512)->nullable();
            $t->string('file_hash_sha256', 64)->nullable()->index();
            $t->unsignedBigInteger('file_size_bytes')->nullable();
            $t->string('file_permissions', 8)->nullable();
            // Process evidence
            $t->unsignedInteger('process_pid')->nullable();
            $t->string('process_name', 128)->nullable();
            $t->string('process_cmdline', 512)->nullable();
            $t->decimal('process_cpu_percent', 6, 2)->nullable();
            $t->unsignedBigInteger('process_rss_kb')->nullable();
            // Network evidence
            $t->string('remote_ip', 45)->nullable();
            $t->unsignedSmallInteger('remote_port')->nullable();
            // Full structured evidence the agent collected — pool URL,
            // worker count, threading hints, etc. Goes verbatim into
            // the suspension ticket as forensic detail.
            $t->json('evidence')->nullable();
            // Status lifecycle:
            //   detected | confirmed | false_positive | suspended | resolved
            $t->string('status', 16)->default('detected')->index();
            $t->text('reviewer_notes')->nullable();
            $t->foreignId('reviewed_by_user_id')->nullable()->constrained('users')->nullOnDelete();
            $t->timestamp('detected_at')->useCurrent();
            $t->timestamp('reviewed_at')->nullable();
            $t->timestamp('resolved_at')->nullable();
            // Optional link to the Incident we opened so the existing
            // notification pipeline (email / Telegram / Slack) fires.
            $t->foreignId('incident_id')->nullable()->constrained('incidents')->nullOnDelete();
            $t->timestamps();

            $t->index(['server_id', 'status', 'detected_at']);
            $t->index(['server_id', 'file_hash_sha256']);
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('agent_security_findings');
        Schema::dropIfExists('agent_security_scans');
    }
};
