<?php

declare(strict_types=1);

use Myvetshop\Module\Clinique\Builder\ShippingBuilder;

require_once __DIR__ . '/CliniqueHolidays.php';

/**
 * Created by Aurélien RICHAUD (16/01/2017 16:24).
 *
 * @deprecated
 * @see Myvetshop\Module\Clinique\Entity\MyvetshopClinique
 */
class Clinique extends ObjectModel
{
    public const TABLE = 'myvetshop_clinique';

    public const CARRIER_DOMICILE = 'domicile';
    public const CARRIER_CLINIQUE = 'clinique';
    public const CARRIER_INCONNU = 'inconnu';

    /**
     * @var array<int, float>
     */
    protected static $reductionsByCat
        = [
            // Alimentation diététique
            33 => 0.030,
            25 => 0.030,
            30 => 0.030,
            // Alimentation médicalisée
            620 => 0.030,
            621 => 0.030,
            // Alimentation physiologique
            27 => 0.100,
            32 => 0.100,
            // Alimentation premium
            615 => 0.100,
            617 => 0.100,
        ];

    /**
     * @var CliniqueHolidays|false|null
     */
    protected $cacheCurrentHoliday = false;

    /**
     * @var int
     */
    public $id_myvetshop_clinique;

    /**
     * @deprecated
     *
     * @var int
     */
    public $id_sso;

    /**
     * @var int
     */
    public $id_employee;

    /**
     * @var int
     */
    public $id_carrier;

    /**
     * @var int
     */
    public $id_carrier_home;

    /**
     * @var int
     */
    public $id_group;

    /**
     * @var int
     */
    public $id_group_rural;

    /**
     * @var int
     */
    public $id_store;

    /**
     * @var int
     */
    public $id_theme;

    /**
     * @var string
     */
    public $name;

    /**
     * @var string
     */
    public $code_privilege;

    /**
     * @var string
     */
    public $centrale;

    /**
     * @var int
     */
    public $id_warehouse;

    /**
     * @var string
     */
    public $code_client;

    /**
     * @var string
     */
    public $mdp;

    /**
     * @var string
     */
    public $code_filiere;

    /**
     * @var string
     */
    public $theme;

    /**
     * @var bool
     */
    public $masquer_logo;

    /**
     * @var bool
     */
    public $email_delivery_notes = true;

    /**
     * @var bool
     */
    public $email_factures = false;

    /**
     * @var string
     */
    public $email_original;

    /**
     * @deprecated
     *
     * @var string
     */
    public $jours_livraison;

    /**
     * @var string
     */
    public $url_rendez_vous;

    /**
     * @var string|null
     */
    public $message;

    /**
     * @var bool
     */
    public $rurale;

    /**
     * @var string
     */
    public $vetoAprenom;

    /**
     * @var string
     */
    public $vetoAnom;

    /**
     * @var string
     */
    public $vetoAurlrdv;

    /**
     * @var string
     */
    public $vetoBprenom;

    /**
     * @var string
     */
    public $vetoBnom;

    /**
     * @var string
     */
    public $vetoBurlrdv;

    /**
     * @var string
     */
    public $vetoCprenom;

    /**
     * @var string
     */
    public $vetoCnom;

    /**
     * @var string
     */
    public $vetoCurlrdv;

    /**
     * @var string
     */
    public $vetoDprenom;

    /**
     * @var string
     */
    public $vetoDnom;

    /**
     * @var string
     */
    public $vetoDurlrdv;

    /**
     * @var string
     */
    public $vetoEprenom;

    /**
     * @var string
     */
    public $vetoEnom;

    /**
     * @var string
     */
    public $vetoEurlrdv;

    /**
     * @var string
     */
    public $apiKey;

    /**
     * @var bool
     */
    public $deleted;

    /**
     * List of product ids separated by commas of products the customers can't add to cart
     *
     * @var string
     */
    public $blockedProducts;

    /**
     * Message displayed to customer when they try to add a blocked product
     *
     * @var string
     */
    public $blockedProductsMessage;

    /**
     * @var bool
     */
    public $don;

    /**
     * @var array{table: string, primary: string}
     *
     * @see ObjectModel::$definition
     */
    public static $definition = [
        'table' => self::TABLE,
        'primary' => 'id_myvetshop_clinique',
        'multilang' => false,
        'multilang_shop' => false,
        'fields' => [
            // ID Externe
            'id_sso' => ['type' => self::TYPE_INT],

            // Clés étrangères
            'id_employee' => ['type' => self::TYPE_INT],
            'id_carrier' => ['type' => self::TYPE_INT],
            'id_carrier_home' => ['type' => self::TYPE_INT],
            'id_group' => ['type' => self::TYPE_INT],
            'id_group_rural' => ['type' => self::TYPE_INT],
            'id_store' => ['type' => self::TYPE_INT],
            'id_theme' => ['type' => self::TYPE_INT],

            // Informations supplémentaires
            'code_privilege' => ['type' => self::TYPE_STRING, 'required' => true, 'size' => 10],
            'centrale' => ['type' => self::TYPE_STRING, 'required' => true, 'size' => 10],
            'id_warehouse' => ['type' => self::TYPE_INT, 'required' => true],
            'code_client' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 10],
            'mdp' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 10],
            'name' => ['type' => self::TYPE_STRING, 'required' => true, 'size' => 255],
            'code_filiere' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 10],

            // Thème
            'theme' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 10],
            'masquer_logo' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false],
            'email_delivery_notes' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],
            'email_factures' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true],

            // E-Mail pour le contact
            'email_original' => ['type' => self::TYPE_STRING, 'required' => false],

            // Informations concernant la livraison
            'jours_livraison' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],

            // URL pour la prise de rendez-vous en ligne
            'url_rendez_vous' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 250],
            'message' => ['type' => self::TYPE_HTML, 'required' => false, 'validate' => 'isCleanHtml', 'size' => 3999999999999],
            'rurale' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false],

            // Flag pour la suppression
            'deleted' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false],

            // Vétérinaire 1
            'vetoAprenom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoAnom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoAurlrdv' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 100],

            // Vétérinaire 2
            'vetoBprenom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoBnom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoBurlrdv' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 100],

            // Vétérinaire 3
            'vetoCprenom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoCnom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoCurlrdv' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 100],

            // Vétérinaire 4
            'vetoDprenom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoDnom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoDurlrdv' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 100],

            // Vétérinaire 5
            'vetoEprenom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoEnom' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 55],
            'vetoEurlrdv' => ['type' => self::TYPE_STRING, 'required' => false, 'size' => 100],

            'apiKey' => ['type' => self::TYPE_STRING, 'required' => true, 'size' => 64],
            'blockedProducts' => ['type' => self::TYPE_HTML, 'required' => false, 'validate' => 'isMessage', 'size' => 3999999999999],
            'blockedProductsMessage' => ['type' => self::TYPE_HTML, 'required' => false, 'validate' => 'isCleanHtml', 'size' => 3999999999999],

            // Activation / désactivation de la possibilité de faire un don
            'don' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false],
        ],
    ];

    public function __construct($id = null, $id_lang = null, $id_shop = null)
    {
        parent::__construct($id, $id_lang, $id_shop);

        if (Validate::isLoadedObject($this)) {
            $carrier = $this->getCarrierDefault();

            // On active/désactive le transporteur "Livraison en clinique" en fonction des vacances de la clinique
            if ($this->isOnHolidays() && $carrier->active) {
                $carrier->active = false;
                $carrier->save();
            }

            if (!$this->isOnHolidays() && !$carrier->active) {
                $carrier->active = true;
                $carrier->save();
            }
        }
    }

    public function willBeOnHolidays(): bool
    {
        return !$this->isOnHolidays() && CliniqueHolidays::hasCliniqueOnHolidaysSoon((int) $this->id);
    }

    public function isOnHolidays(): bool
    {
        return CliniqueHolidays::hasCliniqueOnHolidays((int) $this->id);
    }

    public function getCurrentHoliday(): ?CliniqueHolidays
    {
        if (false === $this->cacheCurrentHoliday) {
            $this->cacheCurrentHoliday = CliniqueHolidays::getCurrentHolliday((int) $this->id);
        }

        return $this->cacheCurrentHoliday;
    }

    public function getHolidayMessage(): ?string
    {
        return CliniqueHolidays::getCliniqueMessage((int) $this->id);
    }

    public function isCliniqueTest(): bool
    {
        return \in_array($this->code_privilege, self::getCodeReserved());
    }

    public function getCarrierDefault(): Carrier
    {
        return new Carrier($this->id_carrier);
    }

    public function getCarrierHome(): Carrier
    {
        return new Carrier($this->id_carrier_home);
    }

    public function getGroupDefault(): Group
    {
        return new Group($this->id_group);
    }

    public function getStore(?int $langId = null): Store
    {
        // @phpstan-ignore-next-line
        return new Store($this->id_store, $langId);
    }

    public function getGroupRural(): ?Group
    {
        return $this->rurale ? new Group($this->id_group_rural) : null;
    }

    /**
     * @return MyvetshopVeterinaire[]
     */
    public function getVeterinaires(): array
    {
        return MyvetshopVeterinaire::getByCliniqueId((int) $this->id);
    }

    public function getShipping(): MyvetshopCliniqueShipping
    {
        return MyvetshopCliniqueShipping::getByCliniqueId((int) $this->id);
    }

    public function hasTheme(): bool
    {
        return $this->id_theme > 0 && null !== $this->getTheme();
    }

    public function getTheme(): ?MyvetshopTheme
    {
        return $this->id_theme ? new MyvetshopTheme($this->id_theme) : null;
    }

    public static function isCodeReserved(string $code): bool
    {
        return in_array(strtoupper($code), self::getCodeReserved());
    }

    /**
     * @return string[]
     */
    public static function getCodeReserved(): array
    {
        return ['AAA111', 'VET123'];
    }

    /**
     * @return Clinique[]
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public static function getAll(): array
    {
        $query = 'SELECT id_myvetshop_clinique FROM ' . _DB_PREFIX_ . self::TABLE;
        $listRaw = Db::getInstance()->executeS($query, true, false);
        if (!\is_array($listRaw)) {
            $listRaw = [];
        }
        $list = \array_column($listRaw, 'id_myvetshop_clinique');

        return \array_map(fn ($cliniqueId) => new self($cliniqueId), $list);
    }

    public function getEmployee(): Employee
    {
        return new Employee($this->id_employee);
    }

    protected function preSave(): void
    {
        // Auto-generate API Key before saving
        if (!$this->apiKey) {
            $this->apiKey = \base_convert(
                \bin2hex(
                    \random_bytes(32)
                ),
                16,
                36
            );
        }

        $this->code_privilege = \strtoupper($this->code_privilege);

        // Employé
        $this->updateEmployee();

        // Groupe privilège
        $this->updateGroupDefault();

        // Groupe rurale
        $this->updateGroupRural();

        // Livraison en clinique
        $this->updateCarrierDefault();

        // Livraison à domicile
        $this->updateCarrierHome();

        // Supprime les associations de groupes inutiles
        Db::getInstance()->execute(
            'DELETE FROM `' . _DB_PREFIX_ . 'carrier_group`'
            . ' WHERE `id_group` = ' . $this->id_group
            . ' AND id_carrier NOT IN (' . $this->id_carrier . ', ' . $this->id_carrier_home . ')',
            false
        );

        // Magasin
        $this->updateStore();

        // Mise à jour des informations du transporteur par défaut
        $carrier = new Carrier($this->id_carrier);
        $carrier->name = $this->name;

        /** @var int[] $languages */
        $languages = Language::getLanguages(true, false, true);
        foreach ($languages as $langId) {
            $carrier->delay[$langId] = ShippingBuilder::getShippingText($this);
        }

        $carrier->save();

        // On associe correctement les groupes
        $this->resetCarrierDefaultGroups();
        $this->resetCarrierHomeGroups();

        // (Ré)-association correcte de l'entrepot
        $this->resetWarehouses();

        // Activation des modules de paiement
        $this->resetPaymentModules();
    }

    /**
     * @param bool $auto_date
     * @param bool $null_values
     *
     * @return bool
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function add($auto_date = true, $null_values = false)
    {
        if (null !== self::getByCodePrivilege($this->code_privilege)) {
            throw new Exception('Le code privilège est déjà utilisé pour une autre clinique.');
        }

        $this->preSave();

        return parent::add($auto_date, $null_values);
    }

    public function update($null_values = false)
    {
        $clinique = self::getByCodePrivilege($this->code_privilege);

        if (null !== $clinique && $clinique->id != $this->id) {
            throw new Exception('Le code privilège est déjà utilisé pour une autre clinique.');
        }

        $this->preSave();

        return parent::update($null_values);
    }

    /**
     * Récupère la liste des commandes de la clinique (par défaut : les 10 dernières).
     *
     * @return Order[]
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function getOrders(int $limit = 10, int $start = 0): array
    {
        $db = Db::getInstance(false);

        $query = 'SELECT o.*
            FROM `' . _DB_PREFIX_ . 'orders` o
            INNER JOIN `' . _DB_PREFIX_ . 'myvetshop_clinique` c ON (`c`.`id_carrier` = `o`.`id_carrier` OR `c`.`id_carrier_home` = `o`.`id_carrier`)
            WHERE `c`.`id_myvetshop_clinique` = ' . (int) $this->id . '
            ORDER BY `o`.`invoice_date` DESC';
        if ($limit) {
            $query .= ' LIMIT ' . ($start ? (int) $start . ', ' : '') . (int) $limit;
        }

        $rows = $db->executeS($query, true, false);
        if (!\is_array($rows)) {
            $rows = [];
        }

        return ObjectModel::hydrateCollection(Order::class, $rows);
    }

    public function getOrderCount(): int
    {
        $db = Db::getInstance(false);

        $query = 'SELECT COUNT(o.id_order) AS `count`
            FROM `' . _DB_PREFIX_ . 'orders` o
            INNER JOIN `' . _DB_PREFIX_ . 'myvetshop_clinique` c ON ('
            . '`c`.`id_carrier` = `o`.`id_carrier` OR `c`.`id_carrier_home` = `o`.`id_carrier`'
            . ')
            WHERE `c`.`id_myvetshop_clinique` = ' . (int) $this->id;

        return (int) $db->getValue($query, false);
    }

    public function hasCustomer(int $customerId): bool
    {
        $customer = new Customer($customerId);

        return in_array($customer->id_default_group, [$this->id_group, $this->id_group_rural]);
    }

    /**
     * Récupère la liste des clients de la clinique.
     *
     * @return \Customer[]
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function getCustomers(int $limit = 10, int $start = 0): array
    {
        $db = Db::getInstance(false);

        $groupIds = [(int) $this->id_group];
        if ($this->id_group_rural) {
            $groupIds[] = (int) $this->id_group_rural;
        }

        $query = 'SELECT c.*
            FROM `' . _DB_PREFIX_ . 'customer` c
            WHERE `c`.`deleted` = 0 AND `c`.`id_default_group` IN (' . implode(', ', $groupIds) . ')
            ORDER BY `c`.`date_add` DESC';
        if ($limit) {
            $query .= ' LIMIT ' . ($start ? (int) $start . ', ' : '') . (int) $limit;
        }

        $rows = $db->executeS($query, true, false);
        if (!\is_array($rows)) {
            $rows = [];
        }

        return ObjectModel::hydrateCollection(Customer::class, $rows);
    }

    public function getCustomerCount(): int
    {
        $db = Db::getInstance(false);

        $groupIds = [(int) $this->id_group];
        if ($this->id_group_rural) {
            $groupIds[] = (int) $this->id_group_rural;
        }

        $query = 'SELECT COUNT(c.id_customer) AS `count`
            FROM `' . _DB_PREFIX_ . 'customer` c
            WHERE `c`.`deleted` = 0 AND `c`.`id_default_group` IN (' . implode(', ', $groupIds) . ')';

        return (int) $db->getValue($query, false);
    }

    /**
     * @return int[]|null
     *
     * @deprecated
     */
    public function getAccessoireCategories(): ?array
    {
        return null;
    }

    public function setPriceCarrierHome(float $price): void
    {
        Db::getInstance()->execute(
            'UPDATE `' . _DB_PREFIX_ . 'delivery`'
            . ' SET `price` = ' . $price
            . ' WHERE `id_carrier` = ' . (int) $this->id_carrier_home,
            false
        );
    }

    public static function getByCliniqueId(int $cliniqueId): ?self
    {
        $query = (new DbQuery())
            ->select(strval(self::$definition['primary']))
            ->from(self::TABLE)
            ->where(strval(self::$definition['primary']) . '=' . $cliniqueId);

        $cliniqueId = Db::getInstance()->getValue($query);

        return $cliniqueId ? new self((int) $cliniqueId) : null;
    }

    public static function get(): ?self
    {
        $context = Context::getContext();
        \assert(null !== $context);
        /** @var Customer|null $customer */
        $customer = $context->customer;

        return $customer ? self::getCliniqueByGroupId((int) $customer->id_default_group) : null;
    }

    public static function getCliniqueByEmployeeId(int $employeeId): ?self
    {
        $query = (new DbQuery())
            ->select(strval(self::$definition['primary']))
            ->from(self::TABLE)
            ->where('id_employee=' . $employeeId);

        $cliniqueId = Db::getInstance()->getValue($query);

        return $cliniqueId ? new self((int) $cliniqueId) : null;
    }

    /**
     * @throws PrestaShopDatabaseException
     */
    public static function getCliniqueIdByCarrier(int $idCarrier): ?int
    {
        $db = Db::getInstance(false);

        $cliniqueId = $db->getValue(
            'SELECT c.id_myvetshop_clinique
                FROM `' . _DB_PREFIX_ . self::TABLE . '` c
                WHERE `c`.`id_carrier` = ' . $idCarrier . ' OR `c`.`id_carrier_home` = ' . $idCarrier
        );

        return $cliniqueId ? (int) $cliniqueId : null;
    }

    public static function getCliniqueByCarrierId(int $carrierId): ?self
    {
        $cliniqueId = self::getCliniqueIdByCarrier($carrierId);

        return null !== $cliniqueId ? new self($cliniqueId) : null;
    }

    /**
     * @throws PrestaShopDatabaseException
     */
    public static function getCliniqueIdByGroup(int $id_group): ?int
    {
        $db = Db::getInstance(false);

        $cliniqueId = $db->getValue(
            'SELECT c.id_myvetshop_clinique
            FROM `' . _DB_PREFIX_ . self::TABLE . '` c
            WHERE `c`.`id_group` = ' . $id_group . ' OR `c`.`id_group_rural` = ' . $id_group
        );

        return $cliniqueId ? (int) $cliniqueId : null;
    }

    public static function getCliniqueByGroupId(int $groupId): ?self
    {
        $query = (new DbQuery())
            ->select(strval(self::$definition['primary']))
            ->from(strval(self::$definition['table']))
            ->where('id_group=' . $groupId . ' OR id_group_rural=' . $groupId);

        $cliniqueId = Db::getInstance()->getValue($query);

        return $cliniqueId ? new self((int) $cliniqueId) : null;
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public static function getByCodePrivilege(string $code): ?self
    {
        $db = Db::getInstance();

        $codeUpper = \strtoupper($code);

        if ('-PRO' === \substr($codeUpper, -4)) {
            $codePrivilege = \substr($codeUpper, 0, -4);
            $pro = true;
        } else {
            $codePrivilege = $codeUpper;
            $pro = false;
        }

        $query = (new DbQuery())
            ->select(strval(self::$definition['primary']))
            ->from(strval(self::$definition['table']))
            ->where('code_privilege="' . $db->escape(\strtoupper($codePrivilege)) . '"');
        if ($pro) {
            $query->where('rurale = 1');
        }
        $cliniqueId = (int) $db->getValue($query);

        $clinique = new self($cliniqueId);

        return Validate::isLoadedObject($clinique) ? $clinique : null;
    }

    /**
     * Détermine si un livreur est "à domicile" ou "en clinique".
     *
     * @return string domicile, clinique ou inconnu
     *
     * @throws PrestaShopDatabaseException
     */
    public static function getCarrierType(int $id_carrier): string
    {
        $clinique_id = self::getCliniqueIdByCarrier($id_carrier);

        if (!$clinique_id) {
            return self::CARRIER_INCONNU;
        }

        $clinique = new self($clinique_id);

        if ($id_carrier == $clinique->id_carrier) {
            return self::CARRIER_CLINIQUE;
        } elseif ($id_carrier == $clinique->id_carrier_home) {
            return self::CARRIER_DOMICILE;
        }

        return self::CARRIER_INCONNU;
    }

    /**
     * @return array<int, float>
     */
    public static function getReductionsByCat(): array
    {
        return self::$reductionsByCat;
    }

    protected function resetPaymentModules(): void
    {
        $db = Db::getInstance();

        $paymentModules = $db->executeS(
            'SELECT id_module FROM `' . _DB_PREFIX_ . 'module` WHERE name IN ("axepta", "myvetshoppayment")'
        );
        if (!\is_array($paymentModules)) {
            $paymentModules = [];
        }

        foreach ($paymentModules as $paymentModule) {
            $db->execute(
                'INSERT IGNORE INTO `' . _DB_PREFIX_ . 'module_carrier`'
                . ' (`id_module`, `id_shop`, `id_reference`)'
                . ' VALUES (' . (int) $paymentModule['id_module'] . ', 1, ' . (int) $this->id_carrier . ')'
                . ', (' . (int) $paymentModule['id_module'] . ', 1, ' . (int) $this->id_carrier_home . ')'
            );
        }
    }

    protected function resetWarehouses(): void
    {
        $db = Db::getInstance();

        $db->execute(
            'DELETE FROM `' . _DB_PREFIX_ . 'warehouse_carrier`'
            . ' WHERE `id_carrier` IN ('
            . $this->id_carrier . ', ' . $this->id_carrier_home
            . ') AND `id_warehouse` != ' . $this->id_warehouse,
            false
        );
        $db->execute(
            'INSERT INTO `' . _DB_PREFIX_ . 'warehouse_carrier` (`id_carrier`, `id_warehouse`)'
            . ' VALUES (' . $this->id_carrier . ', ' . $this->id_warehouse . '), '
            . ' (' . $this->id_carrier_home . ', ' . $this->id_warehouse . ') '
            . ' ON DUPLICATE KEY UPDATE `id_warehouse` = ' . $this->id_warehouse,
            false
        );
    }

    protected function updateEmployee(): void
    {
        // Si le compte employé n'existe pas, on l'ajoute
        if (!$this->id_employee) {
            $employee = new Employee();
            $employee->id_profile = 5;
            $employee->id_lang = 1;
            $employee->firstname = 'Clinique';
            $employee->lastname = 'vétérinaire';
            $employee->email = $this->code_privilege . '@myvetshop.fr';
            $employee->setWsPasswd(\substr($this->code_privilege, 0, 3) . 'cli' . \substr($this->code_privilege, 3));
            if (!$employee->add()) {
                throw new Exception("Can't save employee");
            }

            $this->id_employee = $employee->id;
        }

        // On met à jour le nom et le prénom de l'employee par rapport au nom de la clinique
        $employee = $this->getEmployee();
        $nameArray = \explode(' ', $this->name);

        if (1 == \count($nameArray)) {
            $nameArray[] = 'Clinique vétérinaire';
        }

        $nameArray = \array_filter($nameArray, fn ($value) => '' !== $value);

        $lastname = \array_pop($nameArray);
        $employee->lastname = \preg_replace('#\d#', '', (string) $lastname) ?? '';
        $employee->firstname = \preg_replace('#\d#', '', \implode(' ', $nameArray)) ?? '';
        $employee->save();
    }

    protected function updateGroupDefault(): void
    {
        $context = Context::getContext();
        \assert(null !== $context);
        /** @var int[] $languages */
        $languages = Language::getLanguages(true, false, true);

        // On initie le groupe par défaut avec le code privilège
        if (!$this->id_group) {
            $query = 'SELECT id_group FROM ' . _DB_PREFIX_ . 'group_lang'
                . ' WHERE id_lang=' . $context->language->id . ' AND name="' . $this->code_privilege . '"';
            $groupId = Db::getInstance()->getValue($query, false);

            $group = new Group($groupId ? (int) $groupId : null);

            foreach ($languages as $idLang) {
                $group->name[$idLang] = $this->code_privilege;
            }

            $group->price_display_method = 0;
            if (!$group->add()) {
                throw new Exception("Can't save group");
            }

            $this->id_group = (int) $group->id;
        }

        // On met à jour le nom du groupe par défaut par rapport au code privilège de la clinique
        $groupDefault = $this->getGroupDefault();

        foreach ($languages as $language) {
            $idLang = \is_array($language) ? (int) $language['id_lang'] : $language;
            $groupDefault->name[$idLang] = $this->code_privilege;
        }

        $groupDefault->save();

        // Ajout de la réduction du groupe sur une liste de catégorie
        // @TODO créer une table myvetshop_category_reduction qui va stocker le tableau reductionByCat
        // @TODO ajouter un hook pour modifier le formulaire des catégories et gérer les valeurs
        foreach (static::getReductionsByCat() as $categoryId => $reduction) {
            if (!GroupReduction::doesExist($this->id_group, $categoryId)) {
                $groupeReduction = new GroupReduction();
                $groupeReduction->id_group = $this->id_group;
                $groupeReduction->id_category = $categoryId;
                $groupeReduction->reduction = $reduction;
                $groupeReduction->save();
            }
        }
    }

    protected function updateGroupRural(): void
    {
        $context = Context::getContext();
        \assert(null !== $context);
        /** @var int[] $languages */
        $languages = Language::getLanguages(true, false, true);
        $db = Db::getInstance();

        $rural_ids = [740];
        $last_selected_ids = [740];

        while (count($last_selected_ids)) {
            $ids = $last_selected_ids;
            $last_selected_ids = [];

            $result = $db->executeS(
                'SELECT c.id_category FROM `' . _DB_PREFIX_ . 'category` c'
                . ' WHERE id_parent IN (' . implode(', ', $ids) . ')',
                true,
                false
            );
            if (!is_array($result)) {
                $result = [];
            }

            foreach ($result as $row) {
                $rural_ids[] = (int) $row['id_category'];
                $last_selected_ids[] = (int) $row['id_category'];
            }
        }

        if ($this->rurale) {
            // On cré le groupe rural si celui-ci n'existe pas
            if (!$this->id_group_rural) {
                $name = $this->code_privilege . '-pro';
                $query = 'SELECT id_group FROM ' . _DB_PREFIX_ . 'group_lang'
                    . ' WHERE id_lang=' . (int) $context->language->id . ' AND name="' . $name . '"';
                $groupId = $db->getValue($query, false);

                $group = new Group($groupId ? (int) $groupId : null);

                foreach ($languages as $langId) {
                    $group->name[$langId] = $name;
                }
                // Affichage HT des prix
                $group->price_display_method = 1;
                if (!$group->add()) {
                    throw new Exception("Can't save rural group");
                }

                $this->id_group_rural = (int) $group->id;
            }

            // Désactive l'accès aux catégories de la rurale pour les groupes qui ne sont pas des tarifs privilège
            $db->execute(
                'DELETE FROM `' . _DB_PREFIX_ . 'category_group`'
                . ' WHERE `id_group` <= 3 AND `id_category` IN (' . \implode(', ', $rural_ids) . ')',
                false
            );

            if (!GroupReduction::doesExist($this->id_group_rural, 740)) {
                // Autorise le groupe à accéder à la catégorie
                $query = 'INSERT IGNORE INTO `' . _DB_PREFIX_ . 'category_group` (`id_category`, `id_group`) VALUES ';
                $query .= \implode(
                    ', ',
                    \array_map(
                        fn ($id_cat) => '(' . $id_cat . ', ' . $this->id_group_rural . ')',
                        $rural_ids
                    )
                );
                $db->execute($query, false);

                // Mise en place de la réduction
                $groupeReduction = new GroupReduction();
                $groupeReduction->id_group = $this->id_group_rural;
                $groupeReduction->id_category = 740;
                $groupeReduction->reduction = 0.185;
                $groupeReduction->save();

                // Réductions de groupe
                foreach (self::getReductionsByCat() as $id_category => $reduction) {
                    if (!GroupReduction::doesExist($this->id_group_rural, $id_category)) {
                        $groupeReduction = new GroupReduction();
                        $groupeReduction->id_group = $this->id_group_rural;
                        $groupeReduction->id_category = $id_category;
                        $groupeReduction->reduction = $reduction;
                        $groupeReduction->save();
                    }
                }
            }
        } else {
            if (GroupReduction::doesExist($this->id_group_rural, 740)) {
                $db->execute(
                    'DELETE FROM `' . _DB_PREFIX_ . 'category_group` WHERE'
                    . ' `id_group` = ' . (int) $this->id_group_rural
                    . ' AND `id_category` IN (' . implode(', ', $rural_ids) . ')',
                    false
                );
                $db->execute(
                    'DELETE FROM `' . _DB_PREFIX_ . 'group_reduction` WHERE'
                    . ' `id_group` = ' . (int) $this->id_group_rural . ' AND `id_category` = 740',
                    false
                );
            }
        }
    }

    protected function updateCarrierDefault(): void
    {
        /** @var int[] $languages */
        $languages = Language::getLanguages(true, false, true);

        if (!$this->id_carrier) {
            $carrier = new Carrier();
            $carrier->name = 'Clinique vétérinaire ' . $this->code_privilege;
            $carrier->external_module_name = 'myvetshopclinique';
            $carrier->active = true;
            // @phpstan-ignore-next-line
            $carrier->domicile = 0;
            $carrier->is_module = true;
            $carrier->need_range = true;
            $carrier->shipping_external = true;
            $carrier->is_free = true;
            $carrier->shipping_method = 1;

            foreach ($languages as $idLang) {
                $carrier->delay[$idLang] = ShippingBuilder::getShippingText($this);
            }

            if (!$carrier->add()) {
                throw new Exception("Can't save clinic carrier");
            }
            $this->id_carrier = (int) $carrier->id;

            $carrier->setTaxRulesGroup(0);

            // On associe correctement les groupes de la clinique au transporteur
            $this->resetCarrierDefaultGroups();

            $carrier->addZone(1);

            // Ajout d'une image par défaut
            $defaultImage = _PS_SHIP_IMG_DIR_ . 'img-clinique.jpg';

            if (\file_exists($defaultImage)) {
                \copy(_PS_SHIP_IMG_DIR_ . 'img-clinique.jpg', _PS_SHIP_IMG_DIR_ . $carrier->id . '.jpg');
            }
        }
    }

    protected function updateCarrierHome(): void
    {
        $db = Db::getInstance();

        /** @var int[] $languages */
        $languages = Language::getLanguages(true, false, true);

        // Si le transporteur de livraison à domicile n'existe pas, on le cré

        if (!$this->id_carrier_home) {
            $carrier = new Carrier();
            $carrier->name = 'Livraison à domicile';
            $carrier->active = true;
            // @phpstan-ignore-next-line
            $carrier->domicile = 1;
            $carrier->is_module = true;
            $carrier->need_range = true;
            $carrier->shipping_external = true;
            $carrier->is_free = false;
            $carrier->external_module_name = 'myvetshopclinique';
            $carrier->shipping_method = 1;
            $carrier->max_weight = 40;
            $carrier->range_behavior = 1;

            foreach ($languages as $idLang) {
                $carrier->delay[$idLang] = '4 jours';
            }

            if (!$carrier->add()) {
                throw new Exception("Can't save clinic carrier");
            }
            $this->id_carrier_home = (int) $carrier->id;

            // Zone + RuleGroup
            $carrier->addZone(1);
            $carrier->setTaxRulesGroup(1);

            // On associe correctement les groupes de la clinique au transporteur rural
            $this->resetCarrierHomeGroups();

            // Ajout d'une image par défaut
            $defaultImage = _PS_SHIP_IMG_DIR_ . 'img-domicile.jpg';

            if (\file_exists($defaultImage)) {
                \copy(_PS_SHIP_IMG_DIR_ . 'img-domicile.jpg', _PS_SHIP_IMG_DIR_ . $carrier->id . '.jpg');
            }

            // Mise à jour des tarifs
            $range = new RangeWeight();

            // On ajoute un range jusqu'à 40 Kg
            $range->id_carrier = (int) $carrier->id;
            $range->delimiter1 = 0;
            $range->delimiter2 = 40;

            if (!$range->add()) {
                throw new Exception("Can't save range");
            }

            // On supprime le devilery gratuit créé par défaut
            $db->execute(
                'DELETE FROM `' . _DB_PREFIX_ . 'delivery`'
                . ' WHERE id_range_weight = ' . $range->id,
                false
            );

            // Ajoute le tarif de la configuration ou une par défaut (14 €)
            $price = (int) Configuration::get(
                'MYVETSHOPCLINIQUE_CARRIER_HOME_DEFAULT_PRICE',
                null,
                null,
                null,
                14
            );
            $db->execute(
                'INSERT INTO `' . _DB_PREFIX_ . 'delivery`'
                . ' (id_shop, id_shop_group, id_carrier, id_range_weight, id_zone, price)'
                . ' VALUES (1, 1,' . $carrier->id . ', ' . $range->id . ', 1, ' . $price . ')',
                false
            );
        }
    }

    protected function updateStore(): void
    {
        /** @var int[] $languages */
        $languages = Language::getLanguages(true, false, true);

        // On ajoute une boutique pour la clinique si elle n'existe pas
        if (!$this->id_store) {
            $store = new Store();

            foreach ($languages as $langId) {
                $store->name[$langId] = 'Clinique vétérinaire ' . $this->code_privilege;
                $store->address1[$langId] = 'Inconnu';
            }

            $store->city = 'Paris';
            $store->id_country = 8;
            $store->latitude = 0;
            $store->longitude = 0;
            $store->email = $this->code_privilege . '@myvetshop.fr';
            if (!$store->add()) {
                throw new Exception("Can't save store");
            }

            $this->id_store = $store->id;
        }
    }

    protected function resetCarrierDefaultGroups(): void
    {
        $this->resetCarrierGroups((int) $this->id_carrier);
    }

    protected function resetCarrierHomeGroups(): void
    {
        $this->resetCarrierGroups((int) $this->id_carrier_home);
    }

    protected function resetCarrierGroups(int $carrierId): void
    {
        $carrier = new Carrier($carrierId);

        if (Validate::isLoadedObject($carrier)) {
            $carrierGroups = [$this->id_group];

            if ($this->rurale && $this->id_group_rural) {
                $carrierGroups[] = $this->id_group_rural;
            }

            $carrier->setGroups($carrierGroups);
        }
    }
}
