<?php

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

// Net-new tables needed by the AI Admin Portal feature set:
//   - approval queue / action ledger        (ai_actions)
//   - detected problems & CVE feed          (issues, security_scans, cve_entries, server_cves)
//   - WHMCS ticket mirror + AI replies      (tickets, ticket_replies)
//   - cost ledger + daily roll-up           (ai_costs, ai_daily_spend)
//   - live terminal sessions + watchers     (terminal_sessions, terminal_watchers)
//   - kill switch + freeze flag             (kill_switch_events, system_state)
//   - one-time install tokens               (server_install_tokens)
//   - per-server AI conversation            (server_chat_messages)
//   - audit_logs autonomous results         (server_audit_metrics — SEPARATE from
//     NOC's server_metrics which holds agent-pushed high-cadence telemetry)
//   - KV settings store                     (settings)
//
// audit_logs + anomaly_events are intentionally NOT created here — they live
// on the mysql_audit connection (separate database) via a dedicated migration
// run with --database=mysql_audit. See create_audit_log_tables_on_audit_db.
return new class extends Migration {
    public function up(): void
    {
        // Note: `settings` table not created here — NOC already provides one
        // via 2026_05_07_000009_create_ops_tables.php (key-primary KV with
        // group + encrypted columns). AI Portal services that read/write
        // settings use the same `(group, key)` lookup pattern, so the
        // existing schema serves both pipelines.

        // Approval queue / action ledger.
        Schema::create('ai_actions', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();

            $t->enum('trigger', ['scan', 'cve', 'metric', 'log', 'ticket', 'manual', 'schedule'])
              ->index();
            // Resolved later via AddForeignKeys migration to avoid circular FKs
            // (issues references ai_actions and vice versa).
            $t->foreignId('issue_id')->nullable();
            $t->foreignId('ticket_id')->nullable();

            $t->string('category', 64)->nullable();
            $t->text('command');
            $t->text('reasoning')->nullable();
            $t->enum('risk', ['low', 'medium', 'high', 'critical'])->default('medium')->index();

            $t->string('ai_provider', 32);
            $t->string('ai_model', 64);
            $t->json('ai_metadata')->nullable();

            $t->enum('status', [
                'pending', 'approved', 'rejected', 'modified',
                'running', 'completed', 'failed', 'rolled_back',
            ])->default('pending')->index();

            $t->foreignId('approved_by')->nullable()->constrained('users')->nullOnDelete();
            $t->timestamp('approved_at')->nullable();
            $t->text('rejection_reason')->nullable();

            $t->timestamp('executed_at')->nullable();
            $t->timestamp('completed_at')->nullable();
            $t->longText('output')->nullable();
            $t->integer('exit_code')->nullable();

            $t->text('rollback_command')->nullable();
            $t->string('snapshot_id', 128)->nullable();

            $t->timestamps();

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

        // Detected problems on a server. Bridges NOC incidents (via
        // noc_incident_id) and ai_actions (via resolved_by_action).
        Schema::create('issues', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();

            $t->enum('severity', ['critical', 'high', 'medium', 'low'])->index();
            $t->string('category', 32);
            $t->string('source_tool', 32);
            $t->string('cve_id', 32)->nullable()->index();
            // Cross-link back to NOC's incidents table — same DB, but kept
            // as a soft reference rather than FK so deletes on either side
            // don't cascade unexpectedly.
            $t->unsignedBigInteger('noc_incident_id')->nullable()->index();
            $t->string('noc_public_id', 32)->nullable()->index();

            $t->string('title');
            $t->text('description')->nullable();
            $t->text('suggested_command')->nullable();
            $t->boolean('fixable')->default(false);

            $t->enum('status', ['open', 'acknowledged', 'fixing', 'fixed', 'wont_fix'])
              ->default('open')->index();

            $t->timestamp('detected_at')->index();
            $t->timestamp('resolved_at')->nullable();
            $t->foreignId('resolved_by_action')->nullable()->constrained('ai_actions')->nullOnDelete();

            $t->timestamps();
            $t->index(['server_id', 'status', 'severity']);
        });

        // WHMCS ticket mirror + replies.
        Schema::create('tickets', function (Blueprint $t) {
            $t->id();
            $t->unsignedBigInteger('whmcs_ticket_id')->unique();
            $t->string('whmcs_ticket_num', 32)->index();

            $t->string('subject');
            $t->string('department', 64);
            $t->enum('priority', ['low', 'medium', 'high', 'critical'])->default('medium')->index();

            $t->string('customer_name');
            $t->string('customer_email')->index();
            $t->unsignedBigInteger('whmcs_user_id')->nullable();

            $t->enum('status', [
                'open', 'ai_drafted', 'awaiting_customer', 'auto_replied',
                'staff_replied', 'resolved', 'escalated',
            ])->default('open')->index();

            $t->text('ai_summary')->nullable();
            $t->text('ai_draft_reply')->nullable();
            $t->decimal('ai_confidence', 4, 3)->nullable();
            $t->json('ai_similar_tickets')->nullable();
            $t->string('ai_provider', 32)->nullable();
            $t->string('ai_model', 64)->nullable();

            $t->foreignId('assigned_to')->nullable()->constrained('users')->nullOnDelete();

            $t->timestamp('whmcs_opened_at')->index();
            $t->timestamp('whmcs_last_reply_at')->nullable();
            $t->timestamp('our_last_processed_at')->nullable();

            // Investigator state — moved out of separate ALTER migration to
            // keep all table creation atomic.
            $t->timestamp('investigated_at')->nullable();
            $t->enum('investigation_status', ['pending', 'running', 'completed', 'failed'])->default('pending');
            $t->text('investigation_summary')->nullable();
            $t->string('investigated_server_hostname', 255)->nullable();
            $t->boolean('admin_note_posted')->default(false);

            $t->timestamps();
        });

        Schema::create('ticket_replies', function (Blueprint $t) {
            $t->id();
            $t->foreignId('ticket_id')->constrained()->cascadeOnDelete();
            $t->unsignedBigInteger('whmcs_reply_id')->nullable()->unique();

            $t->enum('author_type', ['customer', 'staff', 'ai_drafted', 'ai_auto'])->index();
            $t->foreignId('staff_user_id')->nullable()->constrained('users')->nullOnDelete();
            $t->string('author_name')->nullable();

            $t->longText('body');
            $t->json('attachments')->nullable();

            $t->string('ai_provider', 32)->nullable();
            $t->string('ai_model', 64)->nullable();
            $t->string('ai_prompt_id', 64)->nullable();
            $t->decimal('ai_confidence', 4, 3)->nullable();
            $t->decimal('ai_cost_usd', 8, 4)->nullable();

            $t->timestamp('sent_at');
            $t->timestamps();
            $t->index(['ticket_id', 'sent_at']);
        });

        // Security scans + CVE feed.
        Schema::create('security_scans', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();
            $t->string('tool', 32)->index();
            $t->enum('status', ['pending', 'running', 'completed', 'failed'])->default('pending');
            $t->timestamp('started_at')->nullable();
            $t->timestamp('completed_at')->nullable();
            $t->longText('raw_output')->nullable();
            $t->unsignedSmallInteger('findings_count')->default(0);
            $t->json('summary')->nullable();
            $t->timestamps();
            $t->index(['server_id', 'tool', 'created_at']);
        });

        Schema::create('cve_entries', function (Blueprint $t) {
            $t->id();
            $t->string('cve_id', 32)->unique();
            $t->enum('severity', ['critical', 'high', 'medium', 'low'])->index();
            $t->decimal('cvss_score', 3, 1)->nullable();
            $t->string('summary', 500)->nullable();
            $t->json('affected_packages')->nullable();
            $t->timestamp('published_at')->index();
            $t->timestamp('checked_at')->nullable();
            $t->timestamps();
        });

        Schema::create('server_cves', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();
            $t->foreignId('cve_entry_id')->constrained('cve_entries')->cascadeOnDelete();
            $t->string('installed_version', 64)->nullable();
            $t->string('fixed_version', 64)->nullable();
            $t->enum('status', ['vulnerable', 'patched', 'mitigated'])->default('vulnerable')->index();
            $t->timestamp('detected_at');
            $t->timestamps();
            $t->unique(['server_id', 'cve_entry_id']);
        });

        // AI cost ledger.
        Schema::create('ai_costs', function (Blueprint $t) {
            $t->id();
            $t->string('provider', 32)->index();
            $t->string('model', 64)->index();
            $t->enum('purpose', [
                'server_action', 'ticket_draft', 'ticket_classify',
                'log_analysis', 'scan_analysis', 'cve_analysis',
                'image_analysis', 'other',
            ])->index();
            $t->morphs('subject');

            $t->unsignedInteger('input_tokens');
            $t->unsignedInteger('output_tokens');
            $t->unsignedInteger('cache_read_tokens')->default(0);
            $t->unsignedInteger('cache_write_tokens')->default(0);
            $t->decimal('cost_usd', 10, 6);

            $t->boolean('zdr')->default(false);
            $t->unsignedSmallInteger('latency_ms')->nullable();

            $t->timestamp('called_at')->index();
            $t->timestamps();
            $t->index(['provider', 'called_at']);
            $t->index(['purpose', 'called_at']);
        });

        Schema::create('ai_daily_spend', function (Blueprint $t) {
            $t->id();
            $t->date('day')->index();
            $t->string('provider', 32);
            $t->decimal('cost_usd', 10, 4);
            $t->unsignedInteger('call_count');
            $t->unsignedBigInteger('input_tokens');
            $t->unsignedBigInteger('output_tokens');
            $t->timestamps();
            $t->unique(['day', 'provider']);
        });

        // Live terminal sessions + watchers.
        Schema::create('terminal_sessions', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();
            $t->foreignId('ai_action_id')->nullable()->constrained()->nullOnDelete();
            $t->string('session_uuid', 36)->unique();
            $t->enum('mode', ['ai', 'admin_takeover'])->default('ai');
            $t->foreignId('controller_user_id')->nullable()->constrained('users')->nullOnDelete();
            $t->enum('status', ['running', 'completed', 'aborted', 'taken_over'])
              ->default('running')->index();
            $t->timestamp('started_at')->index();
            $t->timestamp('ended_at')->nullable();
            $t->unsignedInteger('duration_seconds')->nullable();
            $t->string('recording_path', 255)->nullable();
            $t->unsignedBigInteger('recording_size_bytes')->nullable();
            $t->timestamps();
        });

        Schema::create('terminal_watchers', function (Blueprint $t) {
            $t->id();
            $t->foreignId('terminal_session_id')->constrained()->cascadeOnDelete();
            $t->foreignId('user_id')->constrained()->cascadeOnDelete();
            $t->timestamp('joined_at');
            $t->timestamp('left_at')->nullable();
            $t->timestamps();
        });

        // Kill switch + freeze flag.
        Schema::create('kill_switch_events', function (Blueprint $t) {
            $t->id();
            $t->foreignId('user_id')->constrained()->cascadeOnDelete();
            $t->enum('variant', [
                'pause-ai', 'disconnect-cluster', 'rotate-keys',
                'lock-sessions', 'full',
            ])->index();
            $t->json('scope')->nullable();
            $t->text('reason');
            $t->json('actions_taken')->nullable();
            $t->timestamp('armed_at')->index();
            $t->timestamp('completed_at')->nullable();
            $t->timestamps();
        });

        Schema::create('system_state', function (Blueprint $t) {
            $t->id();
            $t->boolean('ai_frozen')->default(false);
            $t->boolean('all_sessions_locked')->default(false);
            $t->timestamp('frozen_at')->nullable();
            $t->foreignId('frozen_by')->nullable()->constrained('users')->nullOnDelete();
            $t->text('freeze_reason')->nullable();
            $t->timestamps();
        });

        // One-time install tokens for the secure agent onboarding flow.
        Schema::create('server_install_tokens', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->nullable()->constrained()->cascadeOnDelete();
            $t->string('token', 64)->unique();
            $t->foreignId('issued_by')->constrained('users');
            $t->string('expected_hostname')->nullable();
            $t->string('expected_ip', 45)->nullable();
            $t->timestamp('expires_at')->index();
            $t->timestamp('used_at')->nullable();
            $t->string('used_from_ip', 45)->nullable();
            $t->text('agent_public_key')->nullable();
            $t->string('host_key_fingerprint', 128)->nullable();
            $t->timestamps();
        });

        // Per-server AI chat history.
        Schema::create('server_chat_messages', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();
            $t->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
            $t->enum('role', ['user', 'ai', 'system'])->index();
            $t->text('body');
            $t->enum('intent', ['investigate', 'action', 'reply'])->nullable();
            $t->foreignId('ai_action_id')->nullable()->constrained()->nullOnDelete();
            $t->json('attachments')->nullable();
            $t->json('context_used')->nullable();
            $t->timestamps();
            $t->index(['server_id', 'created_at']);
        });

        // Autonomous audit metrics — SEPARATE from NOC's server_metrics, which
        // is the high-cadence agent push table. This one stores the broader
        // snapshot the audit job captures every audit_interval_minutes (default
        // 30 min): services map, swap, exim queue, failed logins, etc.
        Schema::create('server_audit_metrics', function (Blueprint $t) {
            $t->id();
            $t->foreignId('server_id')->constrained()->cascadeOnDelete();
            $t->timestamp('captured_at')->index();

            $t->decimal('cpu_load_1', 6, 2)->nullable();
            $t->decimal('cpu_load_5', 6, 2)->nullable();
            $t->decimal('cpu_load_15', 6, 2)->nullable();
            $t->unsignedTinyInteger('cpu_count')->nullable();

            $t->unsignedInteger('mem_total_mb')->nullable();
            $t->unsignedInteger('mem_used_mb')->nullable();
            $t->unsignedInteger('mem_available_mb')->nullable();
            $t->unsignedInteger('swap_total_mb')->nullable();
            $t->unsignedInteger('swap_used_mb')->nullable();

            $t->decimal('disk_max_used_pct', 5, 2)->nullable();
            $t->json('disks')->nullable();

            $t->unsignedInteger('uptime_seconds')->nullable();
            $t->unsignedSmallInteger('failed_logins_24h')->nullable();
            $t->unsignedSmallInteger('exim_queue')->nullable();

            $t->json('services')->nullable();
            $t->json('extras')->nullable();

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

    public function down(): void
    {
        Schema::dropIfExists('server_audit_metrics');
        Schema::dropIfExists('server_chat_messages');
        Schema::dropIfExists('server_install_tokens');
        Schema::dropIfExists('system_state');
        Schema::dropIfExists('kill_switch_events');
        Schema::dropIfExists('terminal_watchers');
        Schema::dropIfExists('terminal_sessions');
        Schema::dropIfExists('ai_daily_spend');
        Schema::dropIfExists('ai_costs');
        Schema::dropIfExists('server_cves');
        Schema::dropIfExists('cve_entries');
        Schema::dropIfExists('security_scans');
        Schema::dropIfExists('ticket_replies');
        Schema::dropIfExists('tickets');
        Schema::dropIfExists('issues');
        Schema::dropIfExists('ai_actions');
        // settings is not dropped — NOC owns it.
    }
};
