<?php

/**
 * @copyright © Melograno Ventures. All rights reserved.
 * @licence   See LICENCE.md for license details.
 */

namespace AmeliaBooking\Infrastructure\Repository\CustomField;

use AmeliaBooking\Domain\Collection\Collection;
use AmeliaBooking\Domain\Entity\CustomField\CustomField;
use AmeliaBooking\Domain\Factory\CustomField\CustomFieldFactory;
use AmeliaBooking\Domain\Repository\CustomField\CustomFieldRepositoryInterface;
use AmeliaBooking\Infrastructure\Connection;
use AmeliaBooking\Infrastructure\Repository\AbstractRepository;
use AmeliaBooking\Infrastructure\Common\Exceptions\QueryExecutionException;

/**
 * Class CouponRepository
 *
 * @package AmeliaBooking\Infrastructure\Repository\Coupon
 */
class CustomFieldRepository extends AbstractRepository implements CustomFieldRepositoryInterface
{
    public const FACTORY = CustomFieldFactory::class;

    /** @var string */
    private $customFieldsOptionsTable;

    /** @var string */
    private $customFieldsServicesTable;

    /** @var string */
    private $customFieldsEventsTable;

    /** @var string */
    private $servicesTable;

    /** @var string */
    private $eventsTable;


    /**
     * @param Connection $connection
     * @param string     $table
     * @param string     $customFieldsOptionsTable
     * @param string     $customFieldsServicesTable
     * @param string     $serviceTable
     * @param string     $customFieldsEventsTable
     * @param string     $eventTable
     */
    public function __construct(
        Connection $connection,
        $table,
        $customFieldsOptionsTable,
        $customFieldsServicesTable,
        $serviceTable,
        $customFieldsEventsTable,
        $eventTable
    ) {
        parent::__construct($connection, $table);
        $this->customFieldsOptionsTable  = $customFieldsOptionsTable;
        $this->customFieldsServicesTable = $customFieldsServicesTable;
        $this->servicesTable           = $serviceTable;
        $this->customFieldsEventsTable = $customFieldsEventsTable;
        $this->eventsTable = $eventTable;
    }

    /**
     * @param CustomField $entity
     *
     * @return int
     * @throws QueryExecutionException
     */
    public function add($entity)
    {
        $data = $entity->toArray();

        $params = [
            ':label'           => isset($data['label']) ? $data['label'] : '',
            ':type'            => $data['type'],
            ':saveType'        => $data['saveType'],
            ':required'        => $data['required'] ? 1 : 0,
            ':position'        => $data['position'],
            ':translations'    => $data['translations'],
            ':width'           => $data['width'],
            ':saveFirstChoice' => $data['saveFirstChoice'] ? 1 : 0,
            ':includeInInvoice' => $data['includeInInvoice'] ? 1 : 0,
            ':allServices'     => $data['allServices'] ? 1 : 0,
            ':allEvents'       => $data['allEvents'] ? 1 : 0,
            ':useAsLocation'   => $data['useAsLocation'] ? 1 : 0,
        ];

        try {
            $statement = $this->connection->prepare(
                "INSERT INTO
                {$this->table}
                (
                `label`,
                 `type`,
                 `saveType`,
                 `required`,
                 `position`,
                 `translations`,
                 `width`,
                 `saveFirstChoice`,
                 `includeInInvoice`,
                 `allServices`,
                 `allEvents`,
                 `useAsLocation`
                ) VALUES (
                          :label,
                          :type,
                          :saveType,
                          :required,
                          :position,
                          :translations,
                          :width,
                          :saveFirstChoice,
                          :includeInInvoice,
                          :allServices,
                          :allEvents,
                          :useAsLocation
                )"
            );


            $statement->execute($params);
        } catch (\Exception $e) {
            throw new QueryExecutionException('Unable to add data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
        }

        return $this->connection->lastInsertId();
    }

    /**
     * @param int         $id
     * @param CustomField $entity
     *
     * @return bool
     * @throws QueryExecutionException
     */
    public function update($id, $entity)
    {
        $data = $entity->toArray();

        $params = [
            ':label'           => $data['label'],
            ':required'        => $data['required'] ? 1 : 0,
            ':position'        => $data['position'],
            ':translations'    => $data['translations'],
            ':allServices'     => $data['allServices'] ? 1 : 0,
            ':allEvents'       => $data['allEvents'] ? 1 : 0,
            ':useAsLocation'   => $data['useAsLocation'] ? 1 : 0,
            ':saveFirstChoice' => $data['saveFirstChoice'] ? 1 : 0,
            ':width'           => $data['width'] ? : 50,
            ':includeInInvoice' => $data['includeInInvoice'] ? 1 : 0,
            ':id'              => $id,
        ];

        try {
            $statement = $this->connection->prepare(
                "UPDATE {$this->table}
                SET
                `label`    = :label,
                `required` = :required,
                `position` = :position,
                `translations` = :translations,
                `allServices` = :allServices,
                `allEvents` = :allEvents,
                `useAsLocation` = :useAsLocation,
                `saveFirstChoice` = :saveFirstChoice,
                `includeInInvoice` = :includeInInvoice,
                `width` = :width
                WHERE
                id = :id"
            );

            $statement->execute($params);
        } catch (\Exception $e) {
            throw new QueryExecutionException('Unable to save data in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
        }

        return true;
    }

    /**
     * @return Collection|mixed
     * @throws QueryExecutionException
     */
    public function getAll($criteria = [], $getEntities = true)
    {
        $params = [];

        $where = [];

        if (!empty($criteria['eventId'])) {
            $params[':eventId'] = $criteria['eventId'];

            $where[] = 'e.id = :eventId || cf.allEvents = 1';
        }

        $fields = '';

        $joins = '';

        if ($getEntities) {
            $fields .= '
                s.id AS s_id,
                s.name AS s_name,
                s.description AS s_description,
                s.color AS s_color,
                s.price AS s_price,
                s.status AS s_status,
                s.categoryId AS s_categoryId,
                s.minCapacity AS s_minCapacity,
                s.maxCapacity AS s_maxCapacity,
                s.duration AS s_duration,
                e.id AS e_id,
                e.name AS e_name,
                e.price AS e_price,
                e.parentId AS e_parentId,
            ';

            $joins .= "
                LEFT JOIN {$this->customFieldsServicesTable} cfs ON cfs.customFieldId = cf.id
                LEFT JOIN {$this->customFieldsEventsTable} cfe ON cfe.customFieldId = cf.id
                LEFT JOIN {$this->servicesTable} s ON s.id = cfs.serviceId
                LEFT JOIN {$this->eventsTable} e ON e.id = cfe.eventId
            ";
        }

        $fields .= '
            cf.id AS cf_id,
            cf.label AS cf_label,
            cf.type AS cf_type,
            cf.saveType AS cf_saveType,
            cf.required AS cf_required,
            cf.position AS cf_position,
            cf.translations AS cf_translations,
            cf.allServices AS cf_allServices,
            cf.allEvents AS cf_allEvents,
            cf.useAsLocation AS cf_useAsLocation,
            cf.width AS cf_width,
            cf.saveFirstChoice AS cf_saveFirstChoice,
            cf.includeInInvoice AS cf_includeInInvoice,
            cfo.id AS cfo_id,
            cfo.customFieldId AS cfo_custom_field_id,
            cfo.label AS cfo_label,
            cfo.position AS cfo_position,
            cfo.translations AS cfo_translations
        ';

        $where = $where ? 'WHERE ' . implode(' AND ', $where) : '';

        try {
            $statement = $this->connection->prepare(
                "SELECT
                {$fields}
                FROM {$this->table} cf
                LEFT JOIN {$this->customFieldsOptionsTable} cfo ON cfo.customFieldId = cf.id
                {$joins}
                {$where}
                ORDER BY cf.position, cfo.position"
            );

            $statement->execute($params);

            $rows = $statement->fetchAll();
        } catch (\Exception $e) {
            throw new QueryExecutionException('Unable to find by id in ' . __CLASS__ . '. ' . $e->getMessage(), $e->getCode(), $e);
        }

        return call_user_func([static::FACTORY, 'createCollection'], $rows);
    }
}
