<?php

declare(strict_types=1);

namespace UtilityModule\Handler;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Json\Json;

abstract class AbstractCrudHandler implements RequestHandlerInterface
{
    const IDENTIFIER_NAME = 'id';

    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $requestMethod = strtoupper($request->getMethod());
        $id = $request->getAttribute(static::IDENTIFIER_NAME);

        switch ($requestMethod) {
            case 'GET':
                return isset($id)
                    ? $this->get($request)
                    : $this->getList($request);
            case 'POST':
                return $this->create($request);
            case 'PUT':
                return $this->update($request);
            case 'DELETE':
                return isset($id)
                    ? $this->delete($request)
                    : $this->deleteList($request);
            case 'HEAD':
                return $this->head($request);
            case 'OPTIONS':
                return $this->options($request);
            case 'PATCH':
                return $this->patch($request);
            default:
                return $this->createResponse(['content' => 'Method not allowed'], 405);
        }
    }

    public function get(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function getList(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function create(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function update(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function delete(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function deleteList(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function head(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    public function options(ServerRequestInterface $request)
    {
        return new EmptyResponse(200);
    }

    public function patch(ServerRequestInterface $request)
    {
        return $this->createResponse(['content' => 'Method not allowed'], 405);
    }

    final protected function createResponse($data, $status = 200)
    {
        return new JsonResponse($data, $status);
    }

    /**
     *
     * @param ServerRequestInterface $request
     * @return array|object
     */
    public function getRequestData(ServerRequestInterface $request)
    {
        $body = $request->getParsedBody();

        if (!empty($body)) {
            return $body;
        }

        return $this->parseRequestData(
            $request->getBody()->getContents(),
            $request->getHeaderLine('content-type')
        );
    }

    /**
     *
     * @param string $input
     * @param string $contentType
     * @return mixed
     */
    private function parseRequestData($input, $contentType)
    {
        $contentTypeParts = preg_split('/\s*[;,]\s*/', $contentType);
        $parser = $this->returnParserContentType($contentTypeParts[0]);

        return $parser($input);
    }

    /**
     *
     * @param string $contentType
     * @return callable
     */
    private function returnParserContentType(string $contentType): callable
    {
        if ($contentType === 'application/x-www-form-urlencoded') {
            return function ($input) {
                parse_str($input, $data);
                return $data;
            };
        } elseif ($contentType === 'application/json') {
            return function ($input) {
                $jsonDecoder = new Json();
                try {
                    return $jsonDecoder->decode($input, Json::TYPE_ARRAY);
                } catch (\Exception $e) {
                    return false;
                }
            };
        } elseif ($contentType === 'multipart/form-data') {
            return function ($input) {
                return $input;
            };
        }

        return function ($input) {
            return $input;
        };
    }
}
