<?php

namespace App\Livewire;

use App\Models\Server;
use Livewire\Component;

/**
 * Sidebar fleet switcher. Lives in the persistent layout, so it survives
 * wire:navigate. Selecting a server stores it in the session and reloads the
 * current page so every panel reflects the new active server.
 */
class ServerSwitcher extends Component
{
    public function select(string $uuid): void
    {
        $server = Server::where('uuid', $uuid)->firstOrFail();

        session(['active_server_id' => $server->id]);

        $this->redirect($this->targetFor($server, $this->safeReturnUrl()), navigate: true);
    }

    /**
     * Where to land after selecting a server. On a server-details page, switching should
     * VIEW the chosen server (so the URL, sidebar and scoped context never diverge);
     * everywhere else just reload the current page so the active context updates.
     */
    public function targetFor(Server $server, string $current): string
    {
        return preg_match('#^/servers/[^/]+#', $current) === 1
            ? route('servers.show', $server)
            : $current;
    }

    /**
     * Return a SAME-ORIGIN relative target derived from the Referer, or the
     * dashboard. An attacker-supplied Referer must never turn a server switch
     * into an open redirect, so we:
     *   1. reject any Referer whose host differs or whose scheme is not http(s)
     *      (blocks javascript:/data: and off-site hosts), then
     *   2. keep ONLY the path + query and redirect to that rooted relative URL —
     *      so the result can never carry a foreign scheme, host, or port (e.g.
     *      `https://host:444/x` collapses to `/x` on the current origin), and
     *   3. reject protocol-relative (`//host`, `/\host`) and non-rooted values.
     */
    private function safeReturnUrl(): string
    {
        $referer = request()->header('Referer');
        $fallback = route('dashboard');

        if (! is_string($referer) || $referer === '') {
            return $fallback;
        }

        $parts = parse_url($referer);
        if ($parts === false
            || ! isset($parts['host'])
            || strtolower($parts['host']) !== strtolower(request()->getHost())
            || (isset($parts['scheme']) && ! in_array(strtolower($parts['scheme']), ['http', 'https'], true))) {
            return $fallback;
        }

        $target = ($parts['path'] ?? '/').(isset($parts['query']) ? '?'.$parts['query'] : '');

        // Must be a rooted, host-relative path; reject protocol-relative tricks.
        if ($target === '' || $target[0] !== '/'
            || (isset($target[1]) && ($target[1] === '/' || $target[1] === '\\'))) {
            return $fallback;
        }

        return $target;
    }

    public function render()
    {
        $servers = Server::orderBy('name')->get();
        $active = $servers->firstWhere('id', session('active_server_id'))
            ?? $servers->firstWhere('status', 'online')
            ?? $servers->first();

        return view('livewire.server-switcher', [
            'servers' => $servers,
            'active' => $active,
        ]);
    }
}
