<?php

namespace AsyncAws\SsoOidc\Input;

use AsyncAws\Core\Exception\InvalidArgument;
use AsyncAws\Core\Input;
use AsyncAws\Core\Request;
use AsyncAws\Core\Stream\StreamFactory;

final class CreateTokenRequest extends Input
{
    /**
     * The unique identifier string for the client or application. This value comes from the result of the RegisterClient
     * API.
     *
     * @required
     *
     * @var string|null
     */
    private $clientId;

    /**
     * A secret string generated for the client. This value should come from the persisted result of the RegisterClient API.
     *
     * @required
     *
     * @var string|null
     */
    private $clientSecret;

    /**
     * Supports the following OAuth grant types: Authorization Code, Device Code, and Refresh Token. Specify one of the
     * following values, depending on the grant type that you want:
     *
     * * Authorization Code - `authorization_code`
     *
     * * Device Code - `urn:ietf:params:oauth:grant-type:device_code`
     *
     * * Refresh Token - `refresh_token`
     *
     * @required
     *
     * @var string|null
     */
    private $grantType;

    /**
     * Used only when calling this API for the Device Code grant type. This short-lived code is used to identify this
     * authorization request. This comes from the result of the StartDeviceAuthorization API.
     *
     * @var string|null
     */
    private $deviceCode;

    /**
     * Used only when calling this API for the Authorization Code grant type. The short-lived code is used to identify this
     * authorization request.
     *
     * @var string|null
     */
    private $code;

    /**
     * Used only when calling this API for the Refresh Token grant type. This token is used to refresh short-lived tokens,
     * such as the access token, that might expire.
     *
     * For more information about the features and limitations of the current IAM Identity Center OIDC implementation, see
     * *Considerations for Using this Guide* in the IAM Identity Center OIDC API Reference [^1].
     *
     * [^1]: https://docs.aws.amazon.com/singlesignon/latest/OIDCAPIReference/Welcome.html
     *
     * @var string|null
     */
    private $refreshToken;

    /**
     * The list of scopes for which authorization is requested. This parameter has no effect; the access token will always
     * include all scopes configured during client registration.
     *
     * @var string[]|null
     */
    private $scope;

    /**
     * Used only when calling this API for the Authorization Code grant type. This value specifies the location of the
     * client or application that has registered to receive the authorization code.
     *
     * @var string|null
     */
    private $redirectUri;

    /**
     * Used only when calling this API for the Authorization Code grant type. This value is generated by the client and
     * presented to validate the original code challenge value the client passed at authorization time.
     *
     * @var string|null
     */
    private $codeVerifier;

    /**
     * @param array{
     *   clientId?: string,
     *   clientSecret?: string,
     *   grantType?: string,
     *   deviceCode?: string|null,
     *   code?: string|null,
     *   refreshToken?: string|null,
     *   scope?: string[]|null,
     *   redirectUri?: string|null,
     *   codeVerifier?: string|null,
     *   '@region'?: string|null,
     * } $input
     */
    public function __construct(array $input = [])
    {
        $this->clientId = $input['clientId'] ?? null;
        $this->clientSecret = $input['clientSecret'] ?? null;
        $this->grantType = $input['grantType'] ?? null;
        $this->deviceCode = $input['deviceCode'] ?? null;
        $this->code = $input['code'] ?? null;
        $this->refreshToken = $input['refreshToken'] ?? null;
        $this->scope = $input['scope'] ?? null;
        $this->redirectUri = $input['redirectUri'] ?? null;
        $this->codeVerifier = $input['codeVerifier'] ?? null;
        parent::__construct($input);
    }

    /**
     * @param array{
     *   clientId?: string,
     *   clientSecret?: string,
     *   grantType?: string,
     *   deviceCode?: string|null,
     *   code?: string|null,
     *   refreshToken?: string|null,
     *   scope?: string[]|null,
     *   redirectUri?: string|null,
     *   codeVerifier?: string|null,
     *   '@region'?: string|null,
     * }|CreateTokenRequest $input
     */
    public static function create($input): self
    {
        return $input instanceof self ? $input : new self($input);
    }

    public function getClientId(): ?string
    {
        return $this->clientId;
    }

    public function getClientSecret(): ?string
    {
        return $this->clientSecret;
    }

    public function getCode(): ?string
    {
        return $this->code;
    }

    public function getCodeVerifier(): ?string
    {
        return $this->codeVerifier;
    }

    public function getDeviceCode(): ?string
    {
        return $this->deviceCode;
    }

    public function getGrantType(): ?string
    {
        return $this->grantType;
    }

    public function getRedirectUri(): ?string
    {
        return $this->redirectUri;
    }

    public function getRefreshToken(): ?string
    {
        return $this->refreshToken;
    }

    /**
     * @return string[]
     */
    public function getScope(): array
    {
        return $this->scope ?? [];
    }

    /**
     * @internal
     */
    public function request(): Request
    {
        // Prepare headers
        $headers = [
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ];

        // Prepare query
        $query = [];

        // Prepare URI
        $uriString = '/token';

        // Prepare Body
        $bodyPayload = $this->requestBody();
        $body = empty($bodyPayload) ? '{}' : json_encode($bodyPayload, 4194304);

        // Return the Request
        return new Request('POST', $uriString, $query, $headers, StreamFactory::create($body));
    }

    public function setClientId(?string $value): self
    {
        $this->clientId = $value;

        return $this;
    }

    public function setClientSecret(?string $value): self
    {
        $this->clientSecret = $value;

        return $this;
    }

    public function setCode(?string $value): self
    {
        $this->code = $value;

        return $this;
    }

    public function setCodeVerifier(?string $value): self
    {
        $this->codeVerifier = $value;

        return $this;
    }

    public function setDeviceCode(?string $value): self
    {
        $this->deviceCode = $value;

        return $this;
    }

    public function setGrantType(?string $value): self
    {
        $this->grantType = $value;

        return $this;
    }

    public function setRedirectUri(?string $value): self
    {
        $this->redirectUri = $value;

        return $this;
    }

    public function setRefreshToken(?string $value): self
    {
        $this->refreshToken = $value;

        return $this;
    }

    /**
     * @param string[] $value
     */
    public function setScope(array $value): self
    {
        $this->scope = $value;

        return $this;
    }

    private function requestBody(): array
    {
        $payload = [];
        if (null === $v = $this->clientId) {
            throw new InvalidArgument(\sprintf('Missing parameter "clientId" for "%s". The value cannot be null.', __CLASS__));
        }
        $payload['clientId'] = $v;
        if (null === $v = $this->clientSecret) {
            throw new InvalidArgument(\sprintf('Missing parameter "clientSecret" for "%s". The value cannot be null.', __CLASS__));
        }
        $payload['clientSecret'] = $v;
        if (null === $v = $this->grantType) {
            throw new InvalidArgument(\sprintf('Missing parameter "grantType" for "%s". The value cannot be null.', __CLASS__));
        }
        $payload['grantType'] = $v;
        if (null !== $v = $this->deviceCode) {
            $payload['deviceCode'] = $v;
        }
        if (null !== $v = $this->code) {
            $payload['code'] = $v;
        }
        if (null !== $v = $this->refreshToken) {
            $payload['refreshToken'] = $v;
        }
        if (null !== $v = $this->scope) {
            $index = -1;
            $payload['scope'] = [];
            foreach ($v as $listValue) {
                ++$index;
                $payload['scope'][$index] = $listValue;
            }
        }
        if (null !== $v = $this->redirectUri) {
            $payload['redirectUri'] = $v;
        }
        if (null !== $v = $this->codeVerifier) {
            $payload['codeVerifier'] = $v;
        }

        return $payload;
    }
}
