<?php

/**
 * Copyright 2023 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
namespace DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core;

use DeliciousBrains\WP_Offload_Media\Gcp\Google\ApiCore\Serializer;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\ArrayTrait;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\Exception\NotFoundException;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\Exception\ServiceException;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\TimeTrait;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\Cloud\Core\WhitelistTrait;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\Protobuf\Internal\Message;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\ApiCore\ApiException;
use DeliciousBrains\WP_Offload_Media\Gcp\Google\ApiCore\OperationResponse;
/**
 * @internal
 * Responsible for forwarding the requests to their
 * respective client methdos via the request wrapper.
 */
class RequestHandler
{
    use EmulatorTrait;
    use ArrayTrait;
    use TimeTrait;
    use WhitelistTrait;
    use RequestProcessorTrait;
    /**
     * @var Serializer
     */
    private Serializer $serializer;
    private array $clients;
    /**
     * @param Serializer $serializer
     * @param array $clientClasses
     * @param array $clientConfig
     */
    public function __construct(Serializer $serializer, array $clientClasses, array $clientConfig = [])
    {
        //@codeCoverageIgnoreStart
        $this->serializer = $serializer;
        $clientConfig['serializer'] = $serializer;
        // Adds some defaults
        // gccl needs to be present for handwritten clients
        $clientConfig += ['libName' => 'gccl', 'emulatorHost' => null];
        if ((bool) $clientConfig['emulatorHost']) {
            $emulatorConfig = $this->emulatorGapicConfig($clientConfig['emulatorHost']);
            $clientConfig = \array_merge($clientConfig, $emulatorConfig);
        }
        //@codeCoverageIgnoreEnd
        // Initialize the client classes and store them in memory
        $this->clients = [];
        foreach ($clientClasses as $className) {
            $this->clients[$className] = new $className($clientConfig);
        }
    }
    /**
     * Helper function that forwards the request to a client obj.
     *
     * @param string $clientClass The request will be forwarded to this client class.
     * @param string $method This method needs to be called on the client obj.
     * @param Message $request The protobuf Request instance to pass as the first argument to the $method.
     * @param array $optionalArgs The optional args.
     * @param bool $whitelisted This decides the behaviour when a NotFoundException is encountered.
     *
     * @return \Generator|OperationResponse|array|null
     *
     * @throws ServiceException
     */
    public function sendRequest(string $clientClass, string $method, Message $request, array $optionalArgs, bool $whitelisted = \false)
    {
        $clientObj = $this->getClientObject($clientClass);
        if (!$clientObj) {
            return null;
        }
        $allArgs = [$request];
        $allArgs[] = $optionalArgs;
        try {
            $callable = [$clientObj, $method];
            $response = \call_user_func_array($callable, $allArgs);
            return $this->handleResponse($response);
        } catch (ApiException $ex) {
            throw $this->convertToGoogleException($ex);
        } catch (NotFoundException $e) {
            if ($whitelisted) {
                throw $this->modifyWhitelistedError($e);
            }
            throw $e;
        }
    }
    /**
     * Helper function that returns a client object stored in memory
     * using the client class as key.
     * @param $clientClass The client class whose object we need.
     * @return mixed
     */
    private function getClientObject(string $clientClass)
    {
        return $this->clients[$clientClass] ?? null;
    }
}
