<?php

namespace BrizyForms\Service;

use BrizyForms\Exception\ServiceException;
use BrizyForms\FieldMap;
use BrizyForms\Model\Account;
use BrizyForms\Model\Field;
use BrizyForms\Model\Folder;
use BrizyForms\Model\Group;
use BrizyForms\Model\GroupData;
use BrizyForms\Model\RedirectResponse;
use BrizyForms\Model\Response;
use BrizyForms\ServiceConstant;
use BrizyForms\ServiceFactory;
use BrizyForms\Utils\StringUtils;
use SendinBlue\Client\Model\CreateAttribute;

class SendinBlueService extends Service
{
    /**
     * @var \SendinBlue\Client\Configuration
     */
    protected $config;

    /**
     * @param FieldMap $fieldMap
     * @param string $group_id
     *
     * @return mixed
     */
    protected function mapFields(FieldMap $fieldMap, $group_id = null)
    {
        $existCustomFields = json_decode(json_encode($this->internalGetFields()), true);
        foreach ($fieldMap->toArray() as $fieldLink) {
            $attrSlug = strtoupper(StringUtils::getSlug($fieldLink->getSourceTitle()));
            if ($fieldLink->getTarget() == ServiceConstant::AUTO_GENERATE_FIELD) {
                $key_exist = array_search($attrSlug, array_column($existCustomFields, 'name'));
                if ($key_exist !== false) {
                    $fieldLink->setTarget($existCustomFields[$key_exist]['slug']);
                } else {
                    $this->_createField($attrSlug);
                    $fieldLink->setTarget($attrSlug);
                }
            }
        }

        return $fieldMap;
    }

    /**
     * @param FieldMap $fieldMap
     * @param null $group_id
     * @param array $data
     * @param bool $confirmation_email
     * @return mixed|void
     * @throws ServiceException
     * @throws \BrizyForms\Exception\FieldMapException
     */
    protected function internalCreateMember(FieldMap $fieldMap, $group_id = null, array $data = [], $confirmation_email = false)
    {
        $data = $fieldMap->transform($data);

        $payload = [
            'email' => $data->getEmail(),
            'listIds' => [(int)$group_id],
            'updateEnabled' => true
        ];

        if (count($data->getFields()) > 0) {
            $payload['attributes'] = $data->getFields();
        }

        $api_instance = new \SendinBlue\Client\Api\ContactsApi(null, $this->config);
        try {
            $api_instance->createContact(new \SendinBlue\Client\Model\CreateContact($payload));
        } catch (\Exception $e) {
            $this->logger->error($e->getMessage(), ['service' => ServiceFactory::SENDINBLUE, 'method' => 'internalCreateMember']);
            throw new ServiceException($e->getMessage());
        }
    }

    /**
     * @param Folder|null $folder
     * @return array|mixed
     * @throws ServiceException
     */
    protected function internalGetGroups(Folder $folder = null)
    {
        $result = [];
        foreach ($this->_getGroups($folder) as $i => $row) {
            $group = new Group();
            $group
                ->setId($row['id'])
                ->setName($row['name']);

            $result[$i] = $group;
        }

        return $result;
    }

    /**
     * @param Group $group
     *
     * @return mixed
     */
    protected function internalGetFields(Group $group = null)
    {
        $result = [];
        foreach ($this->_getFields() as $i => $customField) {
            $field = new Field();
            $field
                ->setName($customField->getName())
                ->setSlug($customField->getName())
                ->setRequired(false);

            $result[$i] = $field;
        }

        $default = [
            new Field('EMAIL', ServiceConstant::EMAIL_FIELD, true),
        ];

        return array_merge($default, $result);
    }

    private function _getFields()
    {
        $api_instance = new \SendinBlue\Client\Api\AttributesApi(null, $this->config);
        $attributes = $api_instance->getAttributes()->getAttributes();

        $result = [];
        foreach ($attributes as $attribute) {
            if ($attribute->getCategory() == 'normal') {
                $result[] = $attribute;
            }
        }

        return $result;
    }

    private function _createField($name)
    {
        $api_instance = new \SendinBlue\Client\Api\AttributesApi(null, $this->config);
        $createAttribute = new CreateAttribute();
        $createAttribute->setType('text');

        $api_instance->createAttribute('normal', $name, $createAttribute);
    }

    /**
     * @return bool
     */
    protected function hasValidAuthenticationData()
    {
        if (!$this->authenticationData) {
            return false;
        }

        $data = $this->authenticationData->getData();
        if (!isset($data['api_key'])) {
            return false;
        }

        return true;
    }

    /**
     * @return void
     */
    protected function initializeNativeService()
    {
        $data = $this->authenticationData->getData();

        $this->config = \SendinBlue\Client\Configuration::getDefaultConfiguration()->setApiKey('api-key', $data['api_key']);
    }

    /**
     * @param array $options
     * @return RedirectResponse|Response|null
     */
    public function authenticate(array $options = null)
    {
        try {
            $api_instance = new \SendinBlue\Client\Api\ListsApi(null, $this->config);
            $api_instance->getLists()->getLists();
            return new Response(200, 'Successfully authenticated');
        } catch (\Exception $e) {
            return new Response(401, 'Unauthenticated');
        }
    }

    /**
     * @param Folder|null $folder
     * @return array
     * @throws ServiceException
     */
    private function _getGroups(Folder $folder = null)
    {
        try {
            $api_instance = new \SendinBlue\Client\Api\ListsApi(null, $this->config);
            if ($folder) {
                $lists = $api_instance->getFolderLists($folder->getId(),50, 0)->getLists();
            } else {
                $lists = $api_instance->getLists(50, 0)->getLists();
            }
        } catch (\Exception $e) {
            throw new ServiceException('');
        }

        return $lists;
    }

    /**
     * @param GroupData $groupData
     * @return Group|mixed
     * @throws ServiceException
     */
    protected function internalCreateGroup(GroupData $groupData)
    {
        $data = $groupData->getData();

        $api_instance = new \SendinBlue\Client\Api\ListsApi(null, $this->config);
        try {
            $result = $api_instance->createList(new \SendinBlue\Client\Model\CreateList([
                'name'     => $data['name'],
                'folderId' => (int)$data['folder']
            ]));
        } catch (\Exception $e) {
            throw new ServiceException('Group was not created.');
        }

        return new Group($result->getId(), $data['name']);
    }

    /**
     * @param GroupData $groupData
     * @return mixed
     */
    protected function hasValidGroupData(GroupData $groupData)
    {
        $data = $groupData->getData();
        if (!isset($data['name']) || !isset($data['folder'])) {
            return false;
        }

        return true;
    }

    /**
     * @return Account
     * @throws ServiceException
     */
    protected function internalGetAccount()
    {
        try {
            $api_instance = new \SendinBlue\Client\Api\AccountApi(null, $this->config);
            $account = $api_instance->getAccount();
        } catch (\Exception $e) {
            throw new ServiceException('Invalid request');
        }

        return new Account($account['email']);
    }

    /**
     * @return array|null
     * @throws ServiceException
     */
    protected function internalGetFolders()
    {
        try {
            $api_instance = new \SendinBlue\Client\Api\FoldersApi(null, $this->config);
            $folders = $api_instance->getFolders(50,0)->getFolders();
        } catch (\Exception $e) {
            throw new ServiceException('Invalid request');
        }

        $response = [];
        foreach ($folders as $row) {
            $response[] = new Folder($row['id'], $row['name']);
        }

        return $response;
    }

    /**
     * @return array
     * @throws ServiceException
     */
    protected function internalGetGroupProperties()
    {
        return [
            [
                'name' => 'name',
                'title' => 'Name',
                'type' => FieldTypes::TYPE_TEXT,
                'choices' => null
            ],
            [
                'name' => 'folder',
                'title' => 'Folder',
                'type' => FieldTypes::TYPE_SELECT,
                'choices' => $this->internalGetFolders()
            ]
        ];
    }

    /**
     * @return array
     */
    protected function internalGetAccountProperties()
    {
        return [
            [
                'name' => 'api_key',
                'title' => 'Api Key',
                'type' => FieldTypes::TYPE_TEXT
            ]
        ];
    }

    /**
     * @return boolean
     */
    protected function internalHasConfirmation()
    {
        return false;
    }
}