<?php

declare(strict_types=1);

namespace NoahVet\Reef\Security\ResourceType;

use NoahVet\Reef\Exception\NoBearerTokenException;
use NoahVet\Reef\Exception\PermissionFetchException;
use NoahVet\Reef\Factory\ClientFactory;
use NoahVet\Reef\Jane\Model\PermissionGrantList;
use NoahVet\Reef\Security\Authentication\BearerClientHMacComputer;
use NoahVet\Reef\Security\IAM\Model\ResourceType;

class ResourceCollectionPermissionFetcher implements ResourceCollectionPermissionFetcherInterface
{
    public function __construct(
        protected ?string $reefServiceToken,
        protected ClientFactory $clientFactory,
        protected BearerClientHMacComputer $bearerClientHMacComputer,
    ) {
    }

    public function fetch(
        ResourceType $resourceType,
        bool $expandGroups = true,
        ?string $bearerToken = null,
        bool $tokenRemap = true,
    ): ResourceCollectionPermission {
        $token = $bearerToken ?? $this->reefServiceToken;

        if (null === $token) {
            throw new NoBearerTokenException();
        }

        $client = $this->clientFactory->create($token);
        $headerParams = $tokenRemap ? $this->bearerClientHMacComputer->computeClientHMacHeader($token) : [];

        $grantList = $expandGroups ?
            $client->getDetailedPermissionsResourceTypeItem((string) $resourceType, [], $headerParams)
            : $client->getPermissionsResourceTypeItem((string) $resourceType, [], $headerParams);

        if (!$grantList instanceof PermissionGrantList) {
            throw new PermissionFetchException();
        }

        // Find the permissions for all resources
        $allPermissions = [];
        $groupPermissions = [];
        foreach ($grantList->getGroups() as $name => $gPerm) {
            if (\str_ends_with($name, ':*')) {
                $allPermissions = $gPerm->getAllowed();
            } else {
                $groupParts = \explode(':', $name);
                $groupId = \end($groupParts);

                $groupPermissions[$groupId] = $gPerm->getAllowed();
            }
        }

        // Find specific resource permissions
        $resourcePermissions = [];
        foreach ($grantList->getResources() as $id => $rPerm) {
            $resourcePermissions[\str_replace((string) $resourceType.':', '', $id)] = $rPerm->getAllowed();
        }

        return new ResourceCollectionPermission(
            $resourceType,
            $allPermissions,
            $resourcePermissions,
            $groupPermissions,
        );
    }
}
