<?php

declare(strict_types=1);
/**
 * Created by Aurélien RICHAUD (14/05/2018 11:50).
 */
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementMobileStats.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementOrderReturns.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementOrderReturnStates.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementPickupSignatures.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementAnimals.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementFeatures.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementExportProducts.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementMaintenance.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementNotifications.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementRecommandations.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'WebserviceSpecificManagementRecommandationsProducts.php';

class AppWebserviceRequest extends WebserviceRequestCore
{
    /**
     * @var int
     */
    protected $idEmployee;

    /**
     * La clé donne automatiquement accès.
     *
     * @param string $key
     *
     * {@inheritDoc}
     */
    protected function shopHasRight($key): bool
    {
        return true;
    }

    protected function authenticate(): bool
    {
        try {
            $accessToken = OAuthAccessToken::getByToken($this->_key);
        } catch (PrestaShopDatabaseException $e) {
            return false;
        } catch (PrestaShopException $e) {
            return false;
        }

        if (!$accessToken || $accessToken->expires < time()) {
            return false;
        }

        $this->keyPermissions = [
            'addresses' => ['GET', 'HEAD'],
            'carriers' => ['GET', 'HEAD'],
            'cart_rules' => [],
            'carts' => ['GET', 'HEAD'],
            'categories' => ['GET', 'HEAD'],
            'combinations' => ['GET', 'HEAD'],
            'configurations' => [],
            // 'contacts'                       => ['GET', 'HEAD'],
            'content_management_system' => [],
            'countries' => [],
            'currencies' => [],
            /*'customer_messages'              => ['GET', 'HEAD'],
            'customer_threads'               => ['GET', 'HEAD'],*/
            'customers' => ['GET', 'HEAD'],
            'customizations' => [],
            'deliveries' => [],
            'employees' => [],
            'groups' => [],
            'guests' => [],
            'image_types' => [],
            'images' => [],
            'languages' => [],
            'manufacturers' => [],
            'mobile_stats' => ['GET'],
            // 'order_carriers'                 => ['GET', 'HEAD'],
            // 'order_details'                  => ['GET', 'HEAD'],
            // 'order_discounts'                => ['GET', 'HEAD'],
            // 'order_histories'                => ['GET', 'HEAD'],
            // 'order_invoices'                 => ['GET', 'HEAD'],
            // 'order_payments'                 => ['GET', 'HEAD'],
            'order_returns' => ['GET', 'HEAD'],
            'order_return_states' => ['GET', 'HEAD'],
            // 'order_slip'                     => ['GET', 'HEAD'],
            'order_states' => ['GET', 'HEAD'],
            'orders' => ['GET', 'HEAD', 'PUT'],
            'pickup_signatures' => ['GET', 'POST'],
            'price_ranges' => [],
            'product_customization_fields' => [],
            'product_feature_values' => [],
            'product_features' => [],
            'product_option_values' => [],
            'product_options' => [],
            'product_suppliers' => [],
            'products' => ['GET', 'HEAD'],
            'search' => ['GET'],
            'shop_groups' => [],
            'shop_urls' => [],
            'shops' => [],
            'specific_price_rules' => [],
            'specific_prices' => [],
            'states' => [],
            'stock_availables' => [],
            'stock_movement_reasons' => [],
            'stock_movements' => [],
            'stocks' => [],
            'stores' => [],
            'suppliers' => [],
            'supply_order_details' => [],
            'supply_order_histories' => [],
            'supply_order_receipt_histories' => [],
            'supply_order_states' => [],
            'supply_orders' => [],
            'tags' => [],
            'tax_rule_groups' => [],
            'tax_rules' => [],
            'taxes' => [],
            'translated_configurations' => [],
            'warehouse_product_locations' => [],
            'warehouses' => [],
            'weight_ranges' => [],
            'zones' => [],
            'animals' => ['PUT', 'DELETE', 'GET'],
            'features' => ['GET'],
            'export_products' => ['GET', 'POST', 'PUT', 'DELETE'],
            'recommandations' => ['GET', 'POST', 'PUT', 'DELETE'],
            'recommandationsProducts' => ['GET'],
            'maintenance' => ['GET'],
            'notifications' => ['POST'],
        ];

        $this->_authenticated = true;
        $this->idEmployee = (int) $accessToken->id_employee;

        $context = \Context::getContext();
        if ($context) {
            $context->employee = new \Employee($accessToken->id_employee);
        }

        return true;
    }

    /** @phpstan-ignore-next-line */
    protected function manageFilters()
    {
        $filters = parent::manageFilters();

        if (!is_array($filters)) {
            return $filters;
        }

        $clinique = \Clinique::getCliniqueByEmployeeId($this->idEmployee);

        // Filtrage automatique suivant la ressource
        switch ($this->resourceConfiguration['retrieveData']['table']) {
            case 'address':
                if (!$clinique) {
                    throw new \Exception('Clinique non trouvée');
                }

                // Uniquement les adresses de clients de la clinique
                $filters['sql_join'] .= ' INNER JOIN `' . _DB_PREFIX_ . 'customer` c ON `main`.`id_customer` = `c`.`id_customer`';
                $filters['sql_filter'] .= ' AND `c`.`id_default_group` IN (' . (int) $clinique->id_group . ',' . (int) $clinique->id_group_rural . ')';
                break;

            case 'carrier':
                if (!$clinique) {
                    throw new \Exception('Clinique non trouvée');
                }

                // Uniquement les moyens de livraison liés à la clinique
                $filters['sql_filter'] .= ' AND `main`.`id_carrier` IN (' . (int) $clinique->id_carrier . ',' . (int) $clinique->id_carrier_home . ')';
                break;

            case 'cart':
                if (!$clinique) {
                    throw new \Exception('Clinique non trouvée');
                }

                // Uniquement les paniers de clients de la clinique
                $filters['sql_join'] .= ' INNER JOIN `' . _DB_PREFIX_ . 'customer` c ON `main`.`id_customer` = `c`.`id_customer`';
                $filters['sql_filter'] .= ' AND `c`.`id_default_group` IN (' . (int) $clinique->id_group . ',' . (int) $clinique->id_group_rural . ')';
                break;

            case 'customer':
                if (!$clinique) {
                    throw new \Exception('Clinique non trouvée');
                }

                // Uniquement les clients de la clinique
                $filters['sql_filter'] .= ' AND ('
                    . '`main`.`id_default_group` IN (' . (int) $clinique->id_group . ',' . (int) $clinique->id_group_rural . ')'
                    . ' OR (SELECT COUNT(*) FROM ps_orders WHERE ps_orders.id_customer = main.id_customer AND ps_orders.id_carrier IN (' . (int) $clinique->id_carrier . ',' . (int) $clinique->id_carrier_home . '))'
                    . ')';
                $filters['sql_sort'] = str_replace('``.', '`main`.', $filters['sql_sort']);
                break;

            case 'orders':
                if (!$clinique) {
                    throw new \Exception('Clinique non trouvée');
                }

                $filters['sql_filter'] .= ' AND `main`.`id_carrier` IN (' . (int) $clinique->id_carrier . ',' . (int) $clinique->id_carrier_home . ')';
                break;

            case 'category':
            case 'product_attribute':
            default:
                // Rien
        }

        // Retourne les filtres
        return $filters;
    }

    protected function webserviceChecks(): bool
    {
        $ret = parent::webserviceChecks();

        // Je met ça ici parce que je ne sais pas trop où le mettre... ça n'est pas optimal

        if ($ret) {
            $this->objOutput->registerOverrideWSParameters($this, 'customerWSParamater');
        }

        return $ret;
    }

    /**
     * @param int $successReturnCode
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     *
     * {@inheritDoc}
     */
    protected function saveEntityFromXml($successReturnCode): ?bool
    {
        if ('PUT' == $this->method) {
            // TODO : Patch pour empêcher le changement de la date de la facture par l'application mobile
            if (0 === strpos($this->_inputXml, '<prestashop><order>')) {
                $matchs = [];
                if (preg_match('#<id>([0-9]+)</id>#', $this->_inputXml, $matchs)) {
                    $orderId = $matchs[1];
                    $order = new Order((int) $orderId);
                    $this->_inputXml = (string) preg_replace('#<invoice_date>[^<]+</invoice_date>#', '<invoice_date>' . $order->invoice_date . '</invoice_date>', $this->_inputXml);
                }
            }
        }

        return parent::saveEntityFromXml($successReturnCode);
    }

    // ///////////////////////////////////////////
    // / Paramètres particuliers pour l'affichage de certaines entités

    /**
     * @param array{
     *     retrieveData: array{className?: string},
     *     fields: array<string, array<mixed>>,
     *     associations: array<string, array<mixed>>
     * } $ws_params
     *
     * @return array<mixed>
     */
    public function customerWSParamater(array $ws_params): array
    {
        if (isset($ws_params['retrieveData']['className']) && 'Customer' == $ws_params['retrieveData']['className']) {
            $ws_params['fields']['total_orders'] = [
                'sqlId' => 'total_orders',
                'getter' => 'getTotalOrders',
            ];
            $ws_params['fields']['last_visit'] = [
                'sqlId' => 'last_visit',
                'getter' => 'getLastVisit',
            ];
        } elseif (isset($ws_params['retrieveData']['className']) && 'Order' == $ws_params['retrieveData']['className']) {
            $ws_params['fields']['id_carrier'] = ['sqlId' => 'id_carrier', 'xlink_resource' => 'carriers'];
            $ws_params['fields']['date_add']['setter'] = false;
            $ws_params['associations']['order_history'] = [
                'resource' => 'order_state_history',
                'virtual_entity' => true,
                'fields' => [
                    'id_order_history' => [],
                    'invoice' => ['required' => false, 'setter' => false],
                    'send_email' => ['required' => false, 'setter' => false],
                    'color' => ['required' => false, 'setter' => false],
                    'unremovable' => ['required' => false, 'setter' => false],
                    'hidden' => ['required' => false, 'setter' => false],
                    'logable' => ['required' => false, 'setter' => false],
                    'delivery' => ['required' => false, 'setter' => false],
                    'shipped' => ['required' => false, 'setter' => false],
                    'paid' => ['required' => false, 'setter' => false],
                    'pdf_invoice' => ['required' => false, 'setter' => false],
                    'pdf_delivery' => ['required' => false, 'setter' => false],
                    'deleted' => ['required' => false, 'setter' => false],
                    'id_order_state' => ['required' => false, 'setter' => false],
                    'id_employee' => ['required' => false, 'setter' => false],
                    'date_add' => ['required' => false, 'setter' => false],
                    'employee_firstname' => ['required' => false, 'setter' => false],
                    'employee_lastname' => ['required' => false, 'setter' => false],
                    'ostate_name' => ['required' => false, 'setter' => false],
                ],
                'getter' => 'getHistoryWS',
                'setter' => false,
            ];
        }

        return $ws_params;
    }

    /** @phpstan-ignore-next-line */
    public function getFilteredObjectDetails()
    {
        $ret = parent::getFilteredObjectDetails();

        if (count($ret)) {
            $db = Db::getInstance(false);

            $retrieve_data = $this->resourceConfiguration['retrieveData'];

            switch ($retrieve_data['table']) {
                case 'address':
                    /** @var Address $address */
                    $address = $ret[0];
                    /*
                    $customer = new Customer($address->id_customer);
                    $allowed_groups = $db->getRow(
                        "SELECT `myc`.`id_carrier`, `myc`.`id_carrier_home` FROM `" . _DB_PREFIX_ . "myvetshop_clinique` myc WHERE `myc`.`id_employee` = " . (int)$this->_id_employee
                    );
                    // Uniquement les adresses de clients !
                    $filters['sql_join']   .= " INNER JOIN `" . _DB_PREFIX_ . "customer` c ON `main`.`id_customer` = `c`.`id_customer`"
                        . " INNER JOIN `" . _DB_PREFIX_ . "myvetshop_clinique` myc ON `c`.`id_default_group` = `myc`.`id_group`";
                    $filters['sql_filter'] .= ($filters['sql_filter'] ? "" : " AND ") . "`myc`.`id_employee` = " . (int)$this->_id_employee;*/
                    break;

                case 'carrier':
                    /*$filters['sql_join']   .= " INNER JOIN `" . _DB_PREFIX_ . "myvetshop_clinique` myc ON (`main`.`id_carrier` = `myc`.`id_carrier` OR `main`.`id_carrier` = `myc`.`id_carrier_home`)";
                    $filters['sql_filter'] .= ($filters['sql_filter'] ? "" : " AND ") . "`myc`.`id_employee` = " . (int)$this->_id_employee;*/
                    break;

                case 'cart':
                    // Uniquement les paniers de clients !
                    /*$filters['sql_join']   .= " INNER JOIN `" . _DB_PREFIX_ . "customer` c ON `main`.`id_customer` = `c`.`id_customer`"
                        . " INNER JOIN `" . _DB_PREFIX_ . "myvetshop_clinique` myc ON `c`.`id_default_group` = `myc`.`id_group`";
                    $filters['sql_filter'] .= ($filters['sql_filter'] ? "" : " AND ") . "`myc`.`id_employee` = " . (int)$this->_id_employee;*/
                    break;

                case 'orders':
                    /** @var Order $order */
                    $order = $ret[0];
                    $allowed_carriers = $db->getRow(
                        'SELECT `myc`.`id_carrier`, `myc`.`id_carrier_home` FROM `' . _DB_PREFIX_ . 'myvetshop_clinique` myc WHERE `myc`.`id_employee` = ' . (int) $this->idEmployee
                    );

                    if (!is_array($allowed_carriers)) {
                        $allowed_carriers = [];
                    }

                    if (!in_array($order->id_carrier, array_values($allowed_carriers))) {
                        $this->setError(403, "You can't access this object", 16);
                    }
                    break;
            }
        }

        return $ret;
    }

    /**
     * @return array<string, mixed>
     */
    public static function getResources(): array
    {
        $ret = parent::getResources();

        $ret['mobile_stats'] = ['description' => 'Statistiques pour application mobile', 'specific_management' => 'MobileStats'];
        $ret['order_returns'] = ['description' => 'Retours des commandes', 'specific_management' => 'OrderReturns'];
        $ret['order_return_states'] = ['description' => 'Etats des retours des commandes', 'specific_management' => 'OrderReturnStates'];
        $ret['pickup_signatures'] = ['description' => 'Signatures pour le retrait des commandes', 'specific_management' => 'PickupSignature'];
        $ret['animals'] = ['description' => 'Gestion de animaux', 'specific_management' => 'Animals'];
        $ret['features'] = ['description' => 'Récupération des valeurs d\'une features', 'specific_management' => 'Features'];
        $ret['export_products'] = ['description' => 'Gestion des exports pour l\'app veto', 'specific_management' => 'ExportProducts'];
        $ret['recommandations'] = ['description' => 'Gestion des recommandations pour l\'app veto', 'specific_management' => 'Recommandations'];
        $ret['recommandationsProducts'] = ['description' => 'Gestion des recommandations pour l\'app veto', 'specific_management' => 'RecommandationsProducts'];
        $ret['maintenance'] = ['description' => 'Retourne les données de maintenance', 'specific_management' => 'Maintenance'];
        $ret['notifications'] = ['description' => 'Gestion des notifications depuis l\'app veto', 'specific_management' => 'Notifications'];

        return $ret;
    }

    public function getIdEmployee(): int
    {
        return $this->idEmployee;
    }
}
