<?php

if (!defined('ABSPATH')) {
    exit;
}

final class SPP_Permissions
{
    public const META_KEY = 'spp_permissions';

    public const VIEW_PORTAL = 'view_portal';
    public const CREATE_DEPLOYMENTS = 'create_deployments';
    public const START_DEPLOYMENTS = 'start_deployments';
    public const STOP_DEPLOYMENTS = 'stop_deployments';
    public const PROLONG_DEPLOYMENTS = 'prolong_deployments';
    public const REFRESH_DEPLOYMENT_IPS = 'refresh_deployment_ips';
    public const DELETE_DEPLOYMENTS = 'delete_deployments';
    public const MANAGE_ALL_DEPLOYMENTS = 'manage_all_deployments';
    public const MANAGE_TEMPLATES = 'manage_templates';
    public const MANAGE_SETTINGS = 'manage_settings';
    public const MANAGE_PERMISSIONS = 'manage_permissions';

    /**
     * @return array<string, string>
     */
    public static function definitions(): array
    {
        return [
            self::VIEW_PORTAL => 'Open portal and view deployments',
            self::CREATE_DEPLOYMENTS => 'Create deployments',
            self::START_DEPLOYMENTS => 'Start deployments',
            self::STOP_DEPLOYMENTS => 'Stop deployments',
            self::PROLONG_DEPLOYMENTS => 'Prolong deployments',
            self::REFRESH_DEPLOYMENT_IPS => 'Refresh IP addresses',
            self::DELETE_DEPLOYMENTS => 'Delete deployments',
            self::MANAGE_ALL_DEPLOYMENTS => 'View and manage all deployments',
            self::MANAGE_TEMPLATES => 'Manage templates',
            self::MANAGE_SETTINGS => 'Manage Proxmox settings and quotas',
            self::MANAGE_PERMISSIONS => 'Manage user rights',
        ];
    }

    /**
     * @return array<string, array<int, string>>
     */
    public static function groups(): array
    {
        return [
            'Portal' => [
                self::VIEW_PORTAL,
                self::CREATE_DEPLOYMENTS,
            ],
            'Lifecycle' => [
                self::START_DEPLOYMENTS,
                self::STOP_DEPLOYMENTS,
                self::PROLONG_DEPLOYMENTS,
                self::REFRESH_DEPLOYMENT_IPS,
                self::DELETE_DEPLOYMENTS,
            ],
            'Administration' => [
                self::MANAGE_ALL_DEPLOYMENTS,
                self::MANAGE_TEMPLATES,
                self::MANAGE_SETTINGS,
                self::MANAGE_PERMISSIONS,
            ],
        ];
    }

    /**
     * @param array<mixed> $permissions
     * @return array<int, string>
     */
    public static function sanitize_permissions(array $permissions): array
    {
        $valid = array_keys(self::definitions());
        $selected = [];

        foreach ($permissions as $permission) {
            $permission = sanitize_key((string) $permission);
            if (in_array($permission, $valid, true) && !in_array($permission, $selected, true)) {
                $selected[] = $permission;
            }
        }

        $portal_rights = array_diff($selected, [
            self::VIEW_PORTAL,
            self::MANAGE_TEMPLATES,
            self::MANAGE_SETTINGS,
            self::MANAGE_PERMISSIONS,
        ]);

        if (!empty($portal_rights) && !in_array(self::VIEW_PORTAL, $selected, true)) {
            $selected[] = self::VIEW_PORTAL;
        }

        return array_values(array_intersect($valid, $selected));
    }

    public function current_user_has(string $permission): bool
    {
        if (!array_key_exists($permission, self::definitions())) {
            return false;
        }

        if ($this->user_has(get_current_user_id(), $permission)) {
            return true;
        }

        return $this->has_bootstrap_access();
    }

    public function current_user_has_any(): bool
    {
        return !empty($this->current_user_permissions());
    }

    /**
     * @return array<int, string>
     */
    public function current_user_permissions(): array
    {
        if ($this->has_bootstrap_access()) {
            return array_keys(self::definitions());
        }

        return $this->for_user(get_current_user_id());
    }

    public function user_has(int $user_id, string $permission): bool
    {
        if (!array_key_exists($permission, self::definitions())) {
            return false;
        }

        return in_array($permission, $this->for_user($user_id), true);
    }

    /**
     * @return array<int, string>
     */
    public function for_user(int $user_id): array
    {
        if ($user_id < 1) {
            return [];
        }

        $permissions = get_user_meta($user_id, self::META_KEY, true);

        return is_array($permissions) ? self::sanitize_permissions($permissions) : [];
    }

    /**
     * @return array<int, int>
     */
    public function user_ids_with_permission(string $permission): array
    {
        if (!array_key_exists($permission, self::definitions())) {
            return [];
        }

        global $wpdb;

        $like = '%"' . $wpdb->esc_like($permission) . '"%';
        $ids = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT user_id FROM {$wpdb->usermeta} WHERE meta_key = %s AND meta_value LIKE %s",
                self::META_KEY,
                $like
            )
        );

        return array_values(array_map('intval', is_array($ids) ? $ids : []));
    }

    private function has_bootstrap_access(): bool
    {
        return is_user_logged_in()
            && current_user_can('manage_options')
            && empty($this->user_ids_with_permission(self::MANAGE_PERMISSIONS));
    }
}
