<?php

namespace Aws\Api\ErrorParser;

use Aws\Api\Parser\MetadataParserTrait;
use Aws\Api\Parser\PayloadParserTrait;
use Aws\Api\Service;
use Aws\Api\StructureShape;
use Aws\CommandInterface;
use Psr\Http\Message\ResponseInterface;

abstract class AbstractErrorParser {
	use MetadataParserTrait;
	use PayloadParserTrait;

	/**
	 * @var Service
	 */
	protected $api;

	/**
	 * @param Service $api
	 */
	public function __construct( Service $api = null ) {
		$this->api = $api;
	}

	protected function populateShape(
		array &$data,
		ResponseInterface $response,
		CommandInterface $command = null
	) {
		$data['body'] = [];

		if ( ! empty( $command ) && ! empty( $this->api ) ) {

			// If modeled error code is indicated, check for known error shape
			if ( ! empty( $data['code'] ) ) {

				$errors = $this->api->getOperation( $command->getName() )->getErrors();
				foreach ( $errors as $key => $error ) {

					// If error code matches a known error shape, populate the body
					if ( $data['code'] == $error['name']
					     && $error instanceof StructureShape
					) {
						$modeledError = $error;
						$data['body'] = $this->extractPayload(
							$modeledError,
							$response
						);

						foreach ( $error->getMembers() as $name => $member ) {
							switch ( $member['location'] ) {
								case 'header':
									$this->extractHeader( $name, $member, $response, $data['body'] );
									break;
								case 'headers':
									$this->extractHeaders( $name, $member, $response, $data['body'] );
									break;
								case 'statusCode':
									$this->extractStatus( $name, $response, $data['body'] );
									break;
							}
						}

						break;
					}
				}
			}
		}

		return $data;
	}

	protected function extractPayload(
		StructureShape $member,
		ResponseInterface $response
	) {
		if ( $member instanceof StructureShape ) {
			// Structure members parse top-level data into a specific key.
			return $this->payload( $response, $member );
		} else {
			// Streaming data is just the stream from the response body.
			return $response->getBody();
		}
	}
}