<?php

namespace FiloBlu\ActiveCampaign\Model\Resource;

use Exception;
use FiloBlu\ActiveCampaign\Api\Data\JobsInterface;
use FiloBlu\ActiveCampaign\Model\Config\Source\EntityType;
use FiloBlu\ActiveCampaign\Model\Connector\Rest;
use FiloBlu\ActiveCampaign\Helper\Data;
use Magento\Framework\Webapi\Rest\Request;

/**
 *
 */
class ActiveCampaign {

    const ACTIVECAMPAIGN_CREATE_UPDATE_CONTACT_URI = 'contact/sync';
    const ACTIVECAMPAIGN_SUBSCRIPTION_LIST_URI = 'contactLists';
    const ACTIVECAMPAIGN_ORDER_URI = 'ecomOrders';
    const ACTIVECAMPAIGN_CUSTOMER_URI = 'ecomCustomers';
    const ACTIVECAMPAIGN_LISTS_URI = 'lists';
    const ACTIVECAMPAIGN_CONNECTIONS_URI = 'connections';
    const ACTIVECAMPAIGN_FIELDS_URI = 'fields?limit=100';
    const ACTIVECAMPAIGN_BULK_SUBSCRIBER_IMPORT = 'import/bulk_import';

    /**
     * @var \FiloBlu\ActiveCampaign\Helper\Data
     */
    protected $helperData;
    /**
     * @var Rest
     */
    protected $restConnector;
    /**
     * @var array
     */
    protected $validStatuses;

    /**
     * ActiveCampaign constructor.
     * @param Data $helperData
     * @param Rest $restConnector
     * @param array $validStatuses
     */
    public function __construct(
        Data $helperData,
        Rest $restConnector,
        array $validStatuses = []
    ) {
        $this->helperData = $helperData;
        $this->restConnector = $restConnector;
        $this->validStatuses = $validStatuses;
    }

    /**
     * @param $job
     * @return false|\GuzzleHttp\Psr7\Response
     */
    public function createUpdateContact($job) {
        $request = $this->helperData->serializer->unserialize($job['request']);
        $toSend = $request['create_update_contact']['request'];
        if(count($toSend)){
            $this->restConnector->connect();
            return $this->restConnector->doRequest(self::ACTIVECAMPAIGN_CREATE_UPDATE_CONTACT_URI, ['json' => $toSend], Request::HTTP_METHOD_POST);
        }
        return false;
    }

    /**
     * @param $job
     * @return false|\GuzzleHttp\Psr7\Response
     */
    public function subscribeToList($job) {
        $request = $this->helperData->serializer->unserialize($job['request']);
        $toSend = $request['list_subscription']['request'];
        if(!empty($toSend)){
            $this->restConnector->connect();
            return $this->restConnector->doRequest(self::ACTIVECAMPAIGN_SUBSCRIPTION_LIST_URI, ['json' => $toSend], Request::HTTP_METHOD_POST);
        }
        return false;
    }

    /**
     * @param $job
     * @return \GuzzleHttp\Psr7\Response
     * @throws Exception
     */
    public function createOrder($job): \GuzzleHttp\Psr7\Response
    {
        $connectionId = $this->helperData->getConnectionId();
        if(empty($connectionId)) {
            throw new Exception('You need to define a connection to sync an order');
        }
        $request = $this->helperData->serializer->unserialize($job[JobsInterface::REQUEST]);

        $filters = '';
        if($job[JobsInterface::ENTITY] == EntityType::ENTITY_ORDER) {
            $externalId = $request['ecomOrder']['externalid'];
            $filters = "?filters[externalid]={$externalId}&filters[connectionid]=".$connectionId;
        }
        if($job[JobsInterface::ENTITY] == EntityType::ENTITY_QUOTE) {
            $externalCheckoutId = $request['ecomOrder']['externalcheckoutid'];
            $filters = "?filters[externalcheckoutid]={$externalCheckoutId}&filters[connectionid]=".$connectionId;
        }

        if(empty($filters)) {
            throw new Exception('Entity '. $job[JobsInterface::ENTITY].' not recognized');
        }
        $retrieveOrderResponse = $this->retrieveOrder($filters);
        $this->restConnector->connect();
        if($retrieveOrderResponse['meta']['total'] == 0){
            return $this->restConnector->doRequest(self::ACTIVECAMPAIGN_ORDER_URI, ['json' => $request], Request::HTTP_METHOD_POST);
        }
        $orderId = $retrieveOrderResponse['ecomOrders'][0]['id'];
        return $this->restConnector->doRequest(self::ACTIVECAMPAIGN_ORDER_URI.'/'.$orderId, ['json' => $request], Request::HTTP_METHOD_PUT);
    }

    /**
     * @param $filters
     * @return string
     * @throws Exception
     */
    public function retrieveOrder($filters)
    {
        $this->restConnector->connect();
        $response = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_ORDER_URI.$filters);
        if(!in_array($response->getStatusCode(),[200])) {
            throw new Exception($response->getReasonPhrase());
        }
        return $this->helperData->serializer->unserialize($response->getBody()->getContents());
    }

    /**
     * @return array|bool|float|int|string|null
     * @throws Exception
     */
    public function getLists() {
        $this->restConnector->connect();
        $response = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_LISTS_URI);
        if(!in_array($response->getStatusCode(),[200])) {
            throw new Exception($response->getReasonPhrase());
        }
        return $this->helperData->serializer->unserialize($response->getBody()->getContents());
    }

    /**
     * @return array|bool|float|int|string|null
     * @throws Exception
     */
    public function getFields() {
        $this->restConnector->connect();
        $response = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_FIELDS_URI);
        if(!in_array($response->getStatusCode(),[200])) {
            throw new Exception($response->getReasonPhrase());
        }
        return $this->helperData->serializer->unserialize($response->getBody()->getContents());
    }

    /**
     * @param $request
     * @return \GuzzleHttp\Psr7\Response
     * @throws Exception
     */
    public function createConnection($request): \GuzzleHttp\Psr7\Response
    {
        $filters = "?filters[externalid]={$request['connection']['externalid']}";
        $retrieveConnectionResponse = $this->retrieveConnection($filters);
        $this->restConnector->connect();
        if($retrieveConnectionResponse['meta']['total'] == 0){
            return $this->restConnector->doRequest(self::ACTIVECAMPAIGN_CONNECTIONS_URI, ['json' => $request], Request::HTTP_METHOD_POST);
        }
        $connectionId = $retrieveConnectionResponse['connections'][0]['id'];
        return $this->restConnector->doRequest(self::ACTIVECAMPAIGN_CONNECTIONS_URI.'/'.$connectionId, ['json' => $request], Request::HTTP_METHOD_PUT);
    }

    /**
     * @param $filters
     * @return array|bool|float|int|string|null
     * @throws Exception
     */
    public function retrieveConnection($filters) {
        $this->restConnector->connect();
        $response = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_CONNECTIONS_URI.$filters);
        if(!in_array($response->getStatusCode(),[200])) {
            throw new Exception($response->getReasonPhrase());
        }
        return $this->helperData->serializer->unserialize($response->getBody()->getContents());
    }

    /**
     * @param $job
     * @return mixed|string
     * @throws Exception
     */
    public function createCustomer($job): string
    {
        $connectionId = $this->helperData->getConnectionId();
        if(empty($connectionId)) {
            throw new Exception('You need to define a connection to sync a customer');
        }
        $request = $this->helperData->serializer->unserialize($job[JobsInterface::REQUEST]);

        $email = $request['ecomOrder']['email'];
        $encodedEmail = urlencode($email);
        $filters = "?filters[email]={$encodedEmail}&filters[connectionid]=".$connectionId;

        $retrieveCustomerResponse = $this->retrieveCustomer($filters);
        $this->restConnector->connect();
        if($retrieveCustomerResponse['meta']['total'] == 0){
            $req = [
                'ecomCustomer' => [
                    'connectionid'     => $connectionId,
                    'externalid'       => $email,
                    'email'            => $email,
                    'acceptsMarketing' => '1' //TODO CHECK STATUS
                ]
            ];
            $retrieveCustomerCreateResponse = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_CUSTOMER_URI, ['json' => $req], Request::HTTP_METHOD_POST);
            if(!in_array($retrieveCustomerCreateResponse->getStatusCode(),[200,201])) {
                throw new Exception($retrieveCustomerCreateResponse->getReasonPhrase());
            }
            $retrieveCustomerResponse = $this->helperData->serializer->unserialize($retrieveCustomerCreateResponse->getBody()->getContents());
            return $retrieveCustomerResponse['ecomCustomer']['id'];
        }

        return $retrieveCustomerResponse['ecomCustomers'][0]['id'];
    }

    /**
     * @param $filters
     * @return array|bool|float|int|string|null
     * @throws Exception
     */
    public function retrieveCustomer($filters) {
        $this->restConnector->connect();
        $response = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_CUSTOMER_URI.$filters);
        if(!in_array($response->getStatusCode(),[200])) {
            throw new Exception($response->getReasonPhrase());
        }
        return $this->helperData->serializer->unserialize($response->getBody()->getContents());
    }

    /**
     * @param $payload
     * @return mixed
     * @throws Exception
     */
    public function sendSubscriberWithBulk($payload) {
        $this->restConnector->connect();
        $response = $this->restConnector->doRequest(self::ACTIVECAMPAIGN_BULK_SUBSCRIBER_IMPORT,
            ['json' => $payload],
            Request::HTTP_METHOD_POST);

        if (!in_array($response->getStatusCode(), $this->getValidStatuses(__FUNCTION__))) {
            throw new Exception($response->getReasonPhrase());
        }

        return $this->helperData->serializer->unserialize($response->getBody()->getContents());
    }

    /**
     * @param $scope
     * @return array
     */
    public function getValidStatuses($scope): array
    {
        return $this->validStatuses[$scope];
    }
}
