<?php

namespace App\Policies;

use App\Models\Operator;
use App\Models\VpnPeer;

/**
 * Who may see and do what with a VPN access.
 *
 * Ownership is record-level and lives here; permissions describe elevated
 * capabilities (vpn.view.all, vpn.manage.all) and are checked through Gate.
 * The two are kept apart on purpose: seeing an access is not managing it, and
 * neither of them is holding its private key.
 *
 * There is no Gate::before super-admin shortcut in this application, so these
 * rules are the whole story — in particular downloadConfig() cannot be
 * out-voted by a role.
 *
 * Every method here takes an Operator, not a User: a VPN access is a route
 * into the management network, and nothing about it is customer-facing — the
 * owner column has only ever held an operator's id (VpnPeerFactory's default
 * is `Operator::factory()->role('Admin')`, and Vpn::create() only ever writes
 * the id of an operator's own account). There is no second, customer-shaped
 * path to branch this into.
 */
class VpnPeerPolicy
{
    public function viewAny(Operator $operator): bool
    {
        // Every operator may at least reach the page to find their own access.
        return $operator->isOperator();
    }

    public function view(Operator $operator, VpnPeer $peer): bool
    {
        return $this->owns($operator, $peer) || $operator->can('vpn.view.all');
    }

    public function create(Operator $operator): bool
    {
        // Not self-service: an access reaches the management network, so issuing
        // one is an administrative act even for oneself.
        return $operator->can('vpn.manage.all');
    }

    /** Rename, reassign, change stored-config settings. */
    public function update(Operator $operator, VpnPeer $peer): bool
    {
        return $operator->can('vpn.manage.all');
    }

    /** Block / unblock. Owners may switch their own access off and on again. */
    public function block(Operator $operator, VpnPeer $peer): bool
    {
        return $operator->can('vpn.manage.all') || $this->owns($operator, $peer);
    }

    public function delete(Operator $operator, VpnPeer $peer): bool
    {
        return $operator->can('vpn.manage.all') || $this->owns($operator, $peer);
    }

    /**
     * The private half of someone's credential. Owner only — explicitly NOT
     * vpn.view.all and NOT vpn.manage.all: an administrator who needs access
     * revokes this one and issues themselves their own, which keeps the record
     * of who holds what intact.
     */
    public function downloadConfig(Operator $operator, VpnPeer $peer): bool
    {
        return $this->owns($operator, $peer)
            && $peer->config_secret !== null
            && ! $peer->trashed();
    }

    /**
     * isOperator() is part of ownership on purpose: roles can be taken away by
     * other paths than revokeStaff() — a direct role edit, for instance. Losing
     * the console must close these doors by itself, not rely on whoever removed
     * the role also remembering the tunnel.
     */
    private function owns(Operator $operator, VpnPeer $peer): bool
    {
        return $operator->isOperator()
            && $peer->kind === VpnPeer::KIND_STAFF
            && $peer->user_id !== null
            && $peer->user_id === $operator->id;
    }
}
