<?php

namespace CCDN\Helpers;

use CCDN\Helpers\Api\ApiHandler;
use CCDN\Helpers\Api\Response\FranchiseDetailsInterface;
use CCDN\Helpers\Api\ResponseFactory;
use CCDN\Helpers\Entities\Post;
use CCDN\Helpers\Logger\Log;
use CCDN\Helpers\Logger\LogType;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Promise;
use GuzzleHttp\Psr7\Response as GuzzleResponse;

class SearchResolver
{

    /**
     * Priority list for search
     *
     * @var array
     */
    private $priorities;

    /**
     * SearchResolver constructor.
     */
    public function __construct()
    {
        $this->priorities['id'] = Settings::get('ccdn_id_field');
        $this->priorities['kinopoisk_id'] = Settings::get('kinopoisk_id_field');
        $this->priorities['imdb_id'] = Settings::get('imdb_id_field');
        $this->priorities['world_art_id'] = Settings::get('world_art_id_field');
    }

    /**
     * @param  ApiHandler  $api
     * @param  Post[]|null  $posts
     * @return FranchiseDetailsInterface[]
     */
    public function multiHandler(ApiHandler $api, $posts)
    {
        $responses = [];
        $bedResponses = [];
        foreach ($this->priorities as $apiIdType => $customField) {
            if (empty($customField)) {
                continue;
            }

            if (empty($bedResponses)) {
                $promises = $this->_createPromises($api, $posts, $apiIdType, $customField);
            } else {
                $promises = $this->_createPromisesFromFailed($api, $bedResponses, $posts, $apiIdType, $customField);
            }

            $waitResponses = Promise\settle($promises)->wait();
            foreach ($waitResponses as $postId => $item) {
                $responses[$postId] = $item;
            }

            $bedResponses = $this->_getFailed($responses);

        }

        return ResponseFactory::createFranchiseDetail($api->promiseResponseJsonToArray($responses), false);
    }

    /**
     * @param  ApiHandler  $api
     * @param  Post[]  $posts
     * @param  string  $idType
     * @param  string  $field
     * @return array
     */
    private function _createPromises(ApiHandler $api, $posts, $idType, $field)
    {
        $promises = [];
        foreach ($posts as $post) {
            $customFieldValue = $this->_clearIdImdb($idType, $post->getField($field));

            if (empty($customFieldValue))
                continue;

            $promises[$post->id] = $api->getFranchiseDetailsAsync([
                $idType => $customFieldValue
            ]);
        }

        return $promises;
    }

    private function _clearIdImdb($idType, $customFieldValue)
    {
        if ($idType === 'imdb_id') {
            return str_replace('tt', '', $customFieldValue);
        }
        return $customFieldValue;
    }

    /**
     * @param  ApiHandler  $api
     * @param  array  $failed
     * @param  Post[]  $posts
     * @param  string  $idType
     * @param  string  $field
     * @return array
     */
    private function _createPromisesFromFailed(ApiHandler $api, $failed, $posts, $idType, $field)
    {
        $promises = [];
        foreach ($failed as $postId) {
            $customFieldValue = $posts[$postId]->getField($field);

            if (empty($customFieldValue))
                continue;

            $promises[$postId] = $api->getFranchiseDetailsAsync([
                $idType => $customFieldValue
            ]);
        }

        return $promises;
    }

    /**
     * @param  array  $responses
     * @return array
     */
    private function _getFailed($responses)
    {
        $log = new Log();
        $failed = [];
        foreach ($responses as $postId => $response) {

            if ($response['state'] === 'rejected') {
                /** @var ConnectException $connectException */
                $connectException = $response['reason'];
                $log->write(LogType::ACTION_SEARCH,
                    get_class($connectException).': '.$connectException->getMessage().' '.$connectException->getTraceAsString());
                continue;
            }

            /**
             * @var GuzzleResponse $response
             */
            $response = $response['value'];

            if ($response->getStatusCode() !== 200) {
                $failed[] = $postId;
            }
        }

        return $failed;
    }

    /**
     * @param  ApiHandler  $api
     * @param  Post  $post
     * @return FranchiseDetailsInterface|null
     */
    public function singleHandler(ApiHandler $api, Post $post)
    {
        $response = null;
        foreach ($this->priorities as $apiIdType => $customField) {
            if (empty($customField)) {
                continue;
            }
            $customFieldValue = $this->_clearIdImdb($apiIdType, $post->getField($customField));
            $response = $api->getFranchiseDetails([
                $apiIdType => $customFieldValue
            ]);

            if ($response !== null) {
                break;
            }
        }

        return $response;
    }
}
