<?php

namespace objects;

use classes\db;
use classes\object_property;
use Exception;
use modules\subusers\helpers\subusers_permission_node_key;
use Random\RandomException;
use traits\db_object_t;

class subuser_grants_o extends db
{
    use db_object_t;

    public object_property $billing_customer_number;
    public object_property $subuser;
    public object_property $assigned_vehicle_id;
    public object_property $enabled;
    public object_property $note;
    public object_property $permissions;
    public object_property $created_at;
    public object_property $updated_at;
    public object_property $deleted_at;
    const defaultPermissions = [
        'VEHICLES_LIST',
        'SELFSERVE_LIST',
        'SELFSERVE_ADD',
        'BOOKINGS_LIST',
        'BOOKINGS_ADD',
        'ORDERS_LIST',
    ];

    public static function normalizePermissionsValue(mixed $raw): array
    {
        if ($raw === null || $raw === '' || $raw === false || $raw === 0 || $raw === '0') {
            return [];
        }

        if ($raw instanceof subusers_permission_node_key) {
            return [$raw->name];
        }

        if (is_array($raw)) {
            $permissions = [];
            $permissionCandidates = array_is_list($raw)
                ? $raw
                : array_keys(array_filter($raw, static fn ($enabled): bool => (bool)$enabled));

            foreach ($permissionCandidates as $permission) {
                if ($permission instanceof subusers_permission_node_key) {
                    $permission = $permission->name;
                }

                if (!is_string($permission)) {
                    continue;
                }

                $permission = strtoupper(trim($permission));
                if ($permission !== '' && subusers_permission_node_key::tryFrom($permission) !== null) {
                    $permissions[] = $permission;
                }
            }

            return array_values(array_unique($permissions));
        }

        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (json_last_error() === JSON_ERROR_NONE) {
                return self::normalizePermissionsValue($decoded);
            }

            $permission = strtoupper(trim($raw));
            if (subusers_permission_node_key::tryFrom($permission) !== null) {
                return [$permission];
            }
        }

        return [];
    }


    public function structure(): void
    {
        $this->setTable('subuser_grants');
    }

    public function objectChanged(): void
    {
        // No need to invalidate the cache, since the plate_scans object is not cached
    }

    public function getObjectProperties(): void
    {
        $this->billing_customer_number = new object_property($this->table, $this->id, 'billing_customer_number', 'int');
        $this->subuser = new object_property($this->table, $this->id, 'subuser', 'int');
        $this->assigned_vehicle_id = new object_property($this->table, $this->id, 'assigned_vehicle_id', 'int');
        $this->enabled = new object_property($this->table, $this->id, 'enabled', 'bool');
        $this->note = new object_property($this->table, $this->id, 'note', 'string');
        $this->permissions = new object_property($this->table, $this->id, 'permissions', 'json');
        $this->created_at = new object_property($this->table, $this->id, 'created_at', 'timestamp');
        $this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'timestamp');
        $this->deleted_at = new object_property($this->table, $this->id, 'deleted_at', 'timestamp');
    }


    public function asArray(): array
    {
        return [
            'id' => (int)$this->id,
            'billing_customer_number' => (int)$this->billing_customer_number->value(),
            'subuser' => (int)$this->subuser->value(),
            'assigned_vehicle_id' => $this->assigned_vehicle_id->value() !== null ? (int)$this->assigned_vehicle_id->value() : null,
            'enabled' => (bool)$this->enabled->value(),
            'note' => $this->note->value(),
            'permissions' => self::normalizePermissionsValue($this->permissions->value()),
            'created_at' => $this->created_at->value(),
            'updated_at' => $this->updated_at->value(),
            'deleted_at' => $this->deleted_at->value(),
        ];
    }


    /**
     * Add a new subuser grant to the database.
     * @param int $billing_customer_number
     * @param int $subuser
     * @param bool $enabled
     * @param string|null $note
     * @param array|null $permissions
     * @return $this
     * @throws Exception If the object creation fails
     */
    public function add(int $billing_customer_number, int $subuser, bool $enabled, ?string $note, ?array $permissions = self::defaultPermissions): subuser_grants_o
    {
        global $db;
        $permissions = self::normalizePermissionsValue($permissions);
        $tmp = $this->add_object([
            'billing_customer_number' => (int)$billing_customer_number,
            'subuser' => (int)$subuser,
            'enabled' => (bool)$enabled,
            'note' => !empty($note) ? $db->escape_string($note) : null,
            'permissions' => json_encode($permissions, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ]);
        $this->id = (int)$tmp;
        $this->getObjectProperties();
        return $this;
    }

    public function getGrantsForSubuserAndCustomer(int $subuser_id, ?int $customer_number): array
    {
        $grants = self::getFieldsWhere([
            'billing_customer_number' => $customer_number,
            'subuser' => $subuser_id,
            'enabled' => 1,
            'deleted_at' => null,
        ], ['permissions']);
        // Extract permissions from the grants
        $permissions = [];
        foreach ($grants as $grant) {
            $grant_permissions = self::normalizePermissionsValue($grant['permissions'] ?? null);
            if (is_array($grant_permissions)) {
                $permissions = array_merge($permissions, $grant_permissions);
            }
        }
        return array_values(array_unique($permissions));
    }

    public function getGrantForSubuserAndCustomer(int $subuser_id, int $customer_number, bool $includeDisabled = true): ?subuser_grants_o
    {
        $grants = self::getFieldsWhere([
            'billing_customer_number' => $customer_number,
            'subuser' => $subuser_id,
            'deleted_at' => null,
        ], ['id', 'enabled']);

        if (!$includeDisabled) {
            $grants = array_values(array_filter($grants, static fn (array $grant): bool => (int)($grant['enabled'] ?? 0) === 1));
        }

        if (count($grants) === 0) {
            return null;
        }

        usort($grants, static fn (array $left, array $right): int => (int)$right['id'] <=> (int)$left['id']);
        $grant = (new subuser_grants_o())->select((int)$grants[0]['id']);
        $grant->getObjectProperties();
        return $grant;
    }
}
