<?php

namespace App\Notify;

use App\Models\AiAction;
use App\Models\Issue;
use App\Models\Server;

/**
 * Helpers that build a `Message` from a domain object and hand it to the
 * Dispatcher. Caller doesn't have to know channel internals.
 *
 * Wired from:
 *   - Issue model (boot: created)        → critical_alert
 *   - AiAction model (boot: created)     → approval_request
 *   - AiAction model (saving: completed) → action_completed
 *   - AiAction model (saving: failed)    → action_failed
 *   - KillSwitchController                → kill_switch_armed
 */
class EventBus
{
    public function __construct(private Dispatcher $dispatcher) {}

    public function issueDetected(Issue $issue): void
    {
        if (! in_array($issue->severity, ['critical', 'high'], true)) return;

        $server = $issue->server ?? Server::find($issue->server_id);
        $hostname = $server?->hostname ?? '(unknown server)';

        $this->dispatcher->dispatch(new Message(
            event:    'critical_alert',
            title:    "{$hostname}: {$issue->title}",
            body:     trim($issue->description ?? '') ?: '(no details)',
            severity: $issue->severity,
            url:      $this->portalUrl("/incidents"),
            context:  [
                'issue_id'        => $issue->id,
                'server_id'       => $issue->server_id,
                'server_hostname' => $hostname,
                'category'        => $issue->category,
                'cve_id'          => $issue->cve_id,
            ],
        ));
    }

    public function approvalRequested(AiAction $action): void
    {
        $server = $action->server ?? Server::find($action->server_id);
        $hostname = $server?->hostname ?? '(unknown server)';

        // Severity for approval request mirrors the action's risk so push
        // priority is appropriate (critical risk = emergency push).
        $sev = match ($action->risk) {
            'critical' => 'critical',
            'high'     => 'high',
            'medium'   => 'medium',
            default    => 'low',
        };

        $body = "Proposed command:\n  {$action->command}\n\n";
        if ($action->reasoning) $body .= "Reasoning: {$action->reasoning}";

        $this->dispatcher->dispatch(new Message(
            event:    'approval_request',
            title:    "Approval needed: {$hostname}",
            body:     $body,
            severity: $sev,
            url:      $this->portalUrl("/approvals"),
            context:  [
                'action_id' => $action->id,
                'server_id' => $action->server_id,
                'risk'      => $action->risk,
            ],
        ));
    }

    public function actionCompleted(AiAction $action, bool $success): void
    {
        $server = $action->server ?? Server::find($action->server_id);
        $hostname = $server?->hostname ?? '(unknown server)';

        $this->dispatcher->dispatch(new Message(
            event:    $success ? 'action_completed' : 'action_failed',
            title:    ($success ? 'Action completed' : 'Action FAILED') . ": {$hostname}",
            body:     "Command: {$action->command}\nExit code: " . ($action->exit_code ?? '?')
                    . ($action->output ? "\n\nOutput:\n" . substr($action->output, 0, 500) : ''),
            severity: $success ? 'low' : 'high',
            url:      $this->portalUrl("/ai-ops"),
            context:  ['action_id' => $action->id, 'server_id' => $action->server_id],
        ));
    }

    public function killSwitchArmed(string $variant, string $reason, int $userId): void
    {
        $this->dispatcher->dispatch(new Message(
            event:    'kill_switch_armed',
            title:    'KILL SWITCH ARMED — ' . $variant,
            body:     "Operator-initiated. Reason:\n{$reason}",
            severity: 'critical',
            url:      $this->portalUrl("/kill"),
            context:  ['variant' => $variant, 'user_id' => $userId],
        ));
    }

    private function portalUrl(string $path): string
    {
        $base = rtrim(config('app.url'), '/');
        return $base . $path;
    }
}
