<?php

namespace Aws\S3;

use Aws\CommandInterface;
use Psr\Http\Message\RequestInterface;

/**
 * Used to update the host used for S3 requests in the case of using a
 * "bucket endpoint" or CNAME bucket.
 *
 * IMPORTANT: this middleware must be added after the "build" step.
 *
 * @internal
 */
class BucketEndpointMiddleware {
	private static $exclusions = [ 'GetBucketLocation' => true ];
	private $nextHandler;

	public function __construct( callable $nextHandler ) {
		$this->nextHandler = $nextHandler;
	}

	/**
	 * Create a middleware wrapper function.
	 *
	 * @return callable
	 */
	public static function wrap() {
		return function ( callable $handler ) {
			return new self( $handler );
		};
	}

	public function __invoke( CommandInterface $command, RequestInterface $request ) {
		$nextHandler = $this->nextHandler;
		$bucket      = $command['Bucket'];

		if ( $bucket && ! isset( self::$exclusions[ $command->getName() ] ) ) {
			$request = $this->modifyRequest( $request, $command );
		}

		return $nextHandler( $command, $request );
	}

	private function modifyRequest(
		RequestInterface $request,
		CommandInterface $command
	) {
		$uri    = $request->getUri();
		$path   = $uri->getPath();
		$bucket = $command['Bucket'];
		$path   = $this->removeBucketFromPath( $path, $bucket );

		// Modify the Key to make sure the key is encoded, but slashes are not.
		if ( $command['Key'] ) {
			$path = S3Client::encodeKey( rawurldecode( $path ) );
		}

		return $request->withUri( $uri->withPath( $path ) );
	}

	private function removeBucketFromPath( $path, $bucket ) {
		$len = strlen( $bucket ) + 1;
		if ( substr( $path, 0, $len ) === "/{$bucket}" ) {
			$path = substr( $path, $len );
		}

		return $path ?: '/';
	}
}
