<?php

declare(strict_types=1);
/**
 * Created by Aurélien RICHAUD (01/08/2019 12:16)
 */
require_once __DIR__ . DIRECTORY_SEPARATOR . 'ApiFrontController.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . 'Animal.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . 'AnimalSac.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . 'Recommandation.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'class' . DIRECTORY_SEPARATOR . 'CricCrocApi.php';
require_once __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'Service' . DIRECTORY_SEPARATOR . 'RecommandationManagerApi' . DIRECTORY_SEPARATOR . 'RecommandationController.php';

class MyvetshopcliniqueAnimalModuleFrontController extends ApiFrontController
{
    /**
     * @return array<string, mixed>
     *
     * @throws PrestaShopDatabaseException
     */
    protected function serializeAnimal(Animal $animal): array
    {
        $file_name = 'a_' . md5($animal->id . 'animal');
        $photo_url = null;

        if (is_file(_PS_UPLOAD_DIR_ . $file_name . '.jpg')) {
            $photo_url = 'https://' . $_SERVER['HTTP_HOST'] . '/upload/' . $file_name . '.jpg?t=' . filemtime(_PS_UPLOAD_DIR_ . $file_name . '.jpg');
        }

        $recoArray = $this->serializeRecommandation(new Recommandation($animal->getIdRecoCricCroc()));

        $sacs = [];
        $sacsActuels = $animal->getSacsActuels();
        if (is_array($sacsActuels)) {
            $sacs = array_map([$this, 'serializeAnimalSac'], $sacsActuels);
        }

        return [
            'id_animal' => $animal->id,
            'espece' => $animal->espece,
            'nom' => $animal->nom,
            'race' => $animal->race,
            'id_gender' => $animal->id_gender,
            'robe' => $animal->robe,
            'date_naissance' => $animal->date_naissance,
            'ration' => $animal->ration,
            'id_product' => $animal->id_product,
            'date_add' => $animal->date_add,
            'date_upd' => $animal->date_upd,
            'sacs' => $sacs,
            'photo' => $photo_url,
            'reco' => $recoArray,
        ];
    }

    /**
     * @param array<string, mixed> $animalSac
     *
     * @return array<string, mixed>
     */
    protected function serializeAnimalSac(array $animalSac): array
    {
        $cover = Product::getCover($animalSac['id_product']);

        $link = new Link();
        $animalSac['cover_url'] = 'https://' . $link->getImageLink('mobileapp', $cover['id_image'], 'home_default');

        return $animalSac;
    }

    /**
     * @return array<string, mixed>
     */
    protected function serializeRecommandation(Recommandation $reco): array
    {
        $db = Db::getInstance(false);
        $discount_code = null;
        $result = $db->getRow(
            'SELECT `rule`.code AS discount_code 
             FROM `' . _DB_PREFIX_ . 'cart_rule` `rule`
             WHERE `rule`.code = "' . $db->escape($reco->code_reco) . '"
             AND `rule`.quantity > 0
             AND `rule`.active = 1
             ');
        if ($result && is_array($result) && isset($result['discount_code'])) {
            $discount_code = $result['discount_code'];
        }

        return [
            'id' => $reco->id,
            'id_animal' => $reco->id_animal,
            'code_reco' => $reco->code_reco,
            'date_reco' => $reco->date_reco,
            'petname' => $reco->petname,
            'espece' => $reco->espece,
            'race' => $reco->race,
            'id_gender' => $reco->id_gender,
            'activite' => $reco->activite,
            'age' => $reco->age,
            'stade_physio' => $reco->stade_physio,
            'pathologie' => $reco->pathologie,
            'poids_actuel' => $reco->poids_actuel,
            'poids_ideal' => $reco->poids_ideal,
            'be' => $reco->be,
            'nbr_aliment' => $reco->nbr_aliment,
            'product_id_mvs1' => $reco->product_id_mvs1,
            'product_id_mvs2' => $reco->product_id_mvs2,
            'product_id_mvs3' => $reco->product_id_mvs3,
            'qte_aliment1' => $reco->qte_aliment1,
            'qte_aliment2' => $reco->qte_aliment2,
            'qte_aliment3' => $reco->qte_aliment3,
            'transition12' => $reco->transition12,
            'transition23' => $reco->transition23,
            'date_add' => $reco->date_add,
            'date_upd' => $reco->date_upd,
            'deleted' => $reco->deleted,
            'discount_code' => $discount_code,
        ];
    }

    /**
     * @return array<mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionCreate(): array
    {
        $animal = new Animal();
        $animal->hydrate(Tools::getValue('animal'));
        $animal->id_customer = $this->context->customer->id;
        $animal->save();

        if (isset($_FILES['photo'])) {
            $errors = [];

            // Traitement de la mise à jour de la photo
            if ($error = ImageManager::validateUpload($_FILES['photo'], (int) Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE'))) {
                $errors[] = $error;
            }
            if (!($tmp_name = tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['photo']['tmp_name'], $tmp_name)) {
                $errors[] = Tools::displayError('An error occurred during the image upload process.');
            }
            $file_name = 'a_' . md5($animal->id . 'animal');

            if (!$tmp_name) {
                throw new Exception('Le nom du fichier n\'est pas valide');
            }

            $src_info = getimagesize($tmp_name);
            $tmp_name_square = (string) tempnam(_PS_TMP_IMG_DIR_, 'PS');

            if (!is_array($src_info)) {
                throw new Exception('Erreur lors de la récupération des infos de l\'image');
            }

            if (!isset($src_info[0]) || !isset($src_info[1])) {
                throw new Exception('Le format des infos de l\'image est invalide');
            }

            /* @phpstan-ignore-next-line incohérance avec le code source prestashop */
            if (!ImageManager::cut($tmp_name, $tmp_name_square, min($src_info[0], $src_info[1]), min($src_info[0], $src_info[1]))) {
                $errors[] = Tools::displayError("Impossible de retailler l'image");
            }
            if (is_file(_PS_UPLOAD_DIR_ . $file_name . '.jpg')) {
                unlink(_PS_UPLOAD_DIR_ . $file_name . '.jpg');
            }
            if (!ImageManager::resize($tmp_name_square, _PS_UPLOAD_DIR_ . $file_name . '.jpg', 160, 160)) {
                $errors[] = Tools::displayError("Impossible de retailler l'image");
            }
            unlink($tmp_name_square);
            unlink($tmp_name);
        }

        return $this->serializeAnimal($animal);
    }

    /**
     * @return array<string, mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionDelete(): array
    {
        $animal = new Animal(Tools::getValue('id'));

        if ($animal->deleted) {
            header('HTTP/1.0 404 Not Found');

            return [];
        }

        if ($animal->id_customer != $this->context->customer->id) {
            header('HTTP/1.0 403 Forbidden');

            return [];
        }

        $reco = $animal->getReco();

        $animal->deleted = true;
        $result = $animal->save();

        // Si suppression de l'animal, suppression de la reco associé
        if ($result && is_array($reco) && isset($reco['id_recommandation'])) {
            $recommandation = new Recommandation($reco['id_recommandation']);
            $recommandation->deleted = true;
            $recommandation->update();
        }

        return $this->serializeAnimal($animal);
    }

    /**
     * @return array<string, mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionGet(): array
    {
        $animal = new Animal(Tools::getValue('id'));

        if ($animal->id_customer != $this->context->customer->id) {
            header('HTTP/1.0 403 Forbidden');

            return [];
        }

        return $this->serializeAnimal($animal);
    }

    /**
     * @return array<int,mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionList(): array
    {
        $result = Db::getInstance(false)
            ->executeS(
                'SELECT a.*'
                . ' FROM `' . _DB_PREFIX_ . 'animal` a'
                . ' WHERE `a`.`id_customer` = ' . (int) $this->context->customer->id
                . ' AND `a`.`deleted` = 0'
            );

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

        $animaux = ObjectModel::hydrateCollection(
            Animal::class, $result
        );

        $ret = [];

        foreach ($animaux as $animal) {
            $ret[] = $this->serializeAnimal($animal);
        }

        return $ret;
    }

    /**
     * @return array<string, mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionUpdate(): array
    {
        $animal = new Animal(Tools::getValue('id'));

        if ($animal->id_customer != $this->context->customer->id) {
            header('HTTP/1.0 403 Forbidden');

            return [];
        }

        if (Tools::getValue('animal')) {
            $animal->hydrate(Tools::getValue('animal'));
            $animal->id_customer = $this->context->customer->id;
            // todo: date de naissance invalide ?
            $animal->save();
        }

        if (isset($_FILES['photo'])) {
            $errors = [];

            // Traitement de la mise à jour de la photo
            if ($error = ImageManager::validateUpload($_FILES['photo'], (int) Configuration::get('PS_PRODUCT_PICTURE_MAX_SIZE'))) {
                $errors[] = $error;
            }
            if (!($tmp_name = (string) tempnam(_PS_TMP_IMG_DIR_, 'PS')) || !move_uploaded_file($_FILES['photo']['tmp_name'], $tmp_name)) {
                $errors[] = Tools::displayError('An error occurred during the image upload process.');
            }
            $file_name = (string) 'a_' . md5($animal->id . 'animal');

            $src_info = getimagesize((string) $tmp_name);

            if (!is_array($src_info)) {
                throw new Exception('Erreur lors de la récupération des infos de l\'image');
            }

            if (!isset($src_info[0]) || !isset($src_info[1])) {
                throw new Exception('Format des infos de l\'image invalide');
            }

            $tmp_name_square = (string) tempnam(_PS_TMP_IMG_DIR_, 'PS');
            /* @phpstan-ignore-next-line incohérance avec le code source prestashop */
            if (!ImageManager::cut($tmp_name, $tmp_name_square, min($src_info[0], $src_info[1]), min($src_info[0], $src_info[1]))) {
                $errors[] = Tools::displayError("Impossible de retailler l'image");
            }
            if (is_file(_PS_UPLOAD_DIR_ . $file_name . '.jpg')) {
                unlink(_PS_UPLOAD_DIR_ . $file_name . '.jpg');
            }
            if (!ImageManager::resize($tmp_name_square, _PS_UPLOAD_DIR_ . $file_name . '.jpg', 160, 160)) {
                $errors[] = Tools::displayError("Impossible de retailler l'image");
            }
            unlink($tmp_name_square);
            unlink($tmp_name);
        }

        //var_dump($animal);die();
        return $this->serializeAnimal($animal);
    }

    /**
     * @return array<int,array<string, mixed>>|array<string, string>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionSearchSacDetail(): array
    {
        $ret = [];

        $product = new Product(Tools::getValue('id_product'));

        if (!AnimalSac::isCroquette($product->id)) {
            header('HTTP/1.0 400 Bad Request');

            return [
                'error' => "Ce produit n'est pas un sac de croquettes",
            ];
        }

        foreach ($product->getWsCombinations() as $row) {
            $combination = new Combination($row['id']);

            $formats = array_reduce(
                $combination->getAttributesName(1),
                function ($carry, $item) {
                    $carry[] = $item['name'];

                    return $carry;
                },
                []
            );

            $ret[] = [
                'id_order' => null,
                'id_product' => $combination->id_product,
                'id_product_attribute' => $combination->id,
                'date_achat' => null,
                'product_name' => $product->name[Context::getContext()->language->id] . ' - Format : ' . implode(', ', $formats),
                'product_reference' => $combination->reference,
                'product_ean13' => $combination->ean13,
            ];
        }

        return $ret;
    }

    /**
     * @return array<string, mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionAssociate(): array
    {
        $animal = new Animal(Tools::getValue('id'));

        if ($animal->id_customer != $this->context->customer->id) {
            header('HTTP/1.0 403 Forbidden');

            return [];
        }

        $id_order = Tools::getValue('id_order');
        if ($id_order) {
            $order = new Order($id_order);
            if ($order->id_customer != $this->context->customer->id) {
                header('HTTP/1.0 403 Forbidden');

                return [];
            }
        } else {
            $order = new Order();
        }
        $id_product = Tools::getValue('id_product');
        $id_product_attribute = Tools::getValue('id_product_attribute');
        $product_name = Tools::getValue('product_name') . ' - Format : ' . Tools::getValue('product_format');
        $product_reference = Tools::getValue('product_reference');
        $product_ean13 = Tools::getValue('product_ean13');
        $poids_net = 0;

        if ($id_product_attribute) {
            $combination = new Combination($id_product_attribute);
            /* @phpstan-ignore-next-line Attribut absent du code source */
            $poids_net = $combination->net_weight;
        } else {
            $product = new Product($id_product_attribute);
            /* @phpstan-ignore-next-line Attribut absent du code source */
            $poids_net = $product->net_weight;
        }
        $date_achat = $id_order ? $order->invoice_date : date('Y-m-d');

        $animalSac = new AnimalSac();
        $animalSac->id_animal = $animal->id;
        $animalSac->id_product = $id_product;
        $animalSac->id_product_attribute = $id_product_attribute;
        $animalSac->product_name = $product_name;
        $animalSac->product_reference = $product_reference;
        $animalSac->product_ean13 = $product_ean13;
        $animalSac->id_order = $order->id;
        $animalSac->date_achat = $date_achat;
        $animalSac->poids_net = (int) round($poids_net * 1000);
        $animalSac->nombre_rations = (int) floor($animalSac->poids_net / $animal->ration);
        $animalSac->nombre_rations_biais = 0;

        $dateAchat = new DateTime($date_achat);
        $dateAchat->add(new DateInterval('P' . $animalSac->nombre_rations . 'D'));
        $animalSac->date_fin_estimee = $dateAchat->format('Y-m-d H:i:s');

        // Sauvegarde le sac associé à l'animal
        try {
            $animalSac->save();
        } catch (PrestaShopException $e) {
            var_dump($e->getMessage());
        }

        // Retourne l'animal avec ses sacs
        return $this->serializeAnimal($animal);
    }

    /**
     * Mise à jour d'un sac d'un animal
     *
     * @return array<string, mixed>
     *
     * @throws Exception
     */
    protected function updateAnimalSac(): array
    {
        $id_animal_sac = Tools::getValue('id_animal_sac');

        $animalSac = new AnimalSac($id_animal_sac);
        $animal = new Animal($animalSac->id_animal);

        // Vérification de sécurité
        if ($animal->id_customer != $this->context->customer->id) {
            header('HTTP/1.0 403 Forbidden');

            return [];
        }

        // Mise à jour du nombre de rations estimé
        $animalSac->nombre_rations = (int) floor($animalSac->poids_net / $animal->ration);
        // Mise à jour du biais des rations
        $animalSac->nombre_rations_biais = Tools::getValue('nombre_rations_biais');
        $date_fin_estimee = DateTime::createFromFormat('Y-m-d', $animalSac->date_achat);

        if (!$date_fin_estimee instanceof DateTime) {
            throw new Exception('Erreur lors de la récupération de la date de fin estimée');
        }

        $date_fin_estimee->add(new DateInterval('P' . ($animalSac->nombre_rations + $animalSac->nombre_rations_biais) . 'D'));
        $animalSac->date_fin_estimee = $date_fin_estimee->format('Y-m-d');

        // Sauvegarde le sac associé à l'animal
        try {
            $animalSac->save();
        } catch (PrestaShopException $e) {
            var_dump($e->getMessage());
        }

        // Retourne l'animal avec ses sacs
        return $this->serializeAnimal($animal);
    }

    /**
     * @return array<int, array<string, mixed>>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function actionSacListAvailable(): array
    {
        $result = Db::getInstance(false)
            ->executeS(
                'SELECT o.*'
                . ' FROM `' . _DB_PREFIX_ . 'orders` o'
                . ' WHERE `o`.`id_customer` = ' . (int) $this->context->customer->id
                . ' AND `o`.`invoice_date` >= "' . date('Y-m-d', time() - 12 * 31 * 24 * 3600) . '"'
                . ' ORDER BY `o`.`invoice_date` DESC'
            );

        $orders = [];
        if (is_array($result)) {
            /** @var Order[] $orders */
            $orders = ObjectModel::hydrateCollection(
                Order::class, $result
            );
        }

        $ret = [];

        foreach ($orders as $order) {
            $productDetails = $order->getProductsDetail();

            foreach ($productDetails as $productDetail) {
                if (AnimalSac::isCroquette($productDetail['product_id'])) {
                    $ret[] = [
                        'id_order' => $order->id,
                        'id_product' => $productDetail['product_id'],
                        'id_product_attribute' => $productDetail['product_attribute_id'],
                        'date_achat' => $order->invoice_date,
                        'product_name' => $productDetail['product_name'],
                        'product_reference' => $productDetail['product_reference'],
                        'product_ean13' => $productDetail['product_ean13'],
                    ];
                }
            }
        }

        return $ret;
    }

    /**
     * Assign template vars related to page content
     *
     * @throws Exception
     *
     * @see FrontController::initContent()
     */
    public function initContent(): void
    {
        parent::initContent();

        $statusCode = null;
        $recommandationManagerApi = new RecommandationController($this->context->customer);

        try {
            switch (Tools::getValue('action', 'nothing')) {
                case 'create':
                    $ret = $this->actionCreate();
                    break;

                case 'cricCrocImport':
                    if ('_new' !== (string) Tools::getValue('idAnimal')) {
                        $animal = new Animal((int) Tools::getValue('idAnimal'));

                        if (!$animal->id) {
                            $statusCode = 404;
                            throw new Exception('L\'animal n\'existe pas');
                        }

                        if ($animal->id_customer != $this->context->customer->id) {
                            $statusCode = 401;
                            throw new Exception('L\'animal n\'appartient pas au client');
                        }
                    }

                    $ret = $recommandationManagerApi->actionCricCrocImport(trim(strtoupper(Tools::getValue('code'))), (string) Tools::getValue('idAnimal')); // string car peut être '_new'
                    break;

                case 'cricCrocList':
                    $ret = $recommandationManagerApi->actionCricCrocList();
                    break;

                case 'cricCrocGet':
                    $recoId = Tools::getValue('recoId');
                    if (!$recoId) {
                        $statusCode = 400;
                        throw new Exception('La requête n\'est pas valide');
                    }

                    $ret = $recommandationManagerApi->actionCricCrocGetRecoById((int) $recoId);
                    break;

                case 'cricCrocDelete':
                    $recommandation = new Recommandation((int) Tools::getValue('idReco'));

                    if (!$recommandation->id) {
                        $statusCode = 404;
                        throw new Exception('La recommandation n\'existe pas');
                    }

                    if ($recommandation->id_customer != $this->context->customer->id) {
                        $statusCode = 401;
                        throw new Exception('La recommandation n\'appartient pas au client');
                    }

                    $ret = $recommandationManagerApi->actionCricCrocDelete($recommandation->id);
                    break;

                case 'cricCrocUpdate':
                    $recommandation = new Recommandation((int) Tools::getValue('idReco'));

                    if (!$recommandation->id || $recommandation->deleted) {
                        $statusCode = 404;
                        throw new Exception('La recommandation n\'existe pas');
                    }

                    $animal = new Animal((int) Tools::getValue('idAnimal'));

                    if (!$animal->id) {
                        $statusCode = 404;
                        throw new Exception('L\'animal n\'existe pas');
                    }

                    if ($animal->id_customer != $this->context->customer->id) {
                        $statusCode = 401;
                        throw new Exception('L\'animal n\'appartient pas au client');
                    }

                    $ret = $recommandationManagerApi->actionCricCrocUpdate($recommandation->id, $animal->id, (int) Tools::getValue('idProduct'), (int) Tools::getValue('quantity'));
                    break;

                case 'recommandationPdf':
                    $recommandation = new Recommandation((int) Tools::getValue('idReco'));

                    if (!$recommandation->id) {
                        $statusCode = 404;
                        throw new Exception('La recommandation n\'existe pas');
                    }

                    if ($recommandation->id_customer != $this->context->customer->id) {
                        $statusCode = 401;
                        throw new Exception('La recommandation n\'appartient pas au client');
                    }

                    $recommandationManagerApi->actionRecommandationOpenPdf($recommandation->id);
                    break;

                case 'cricCrocCheckCodeReco':
                    $ret = $recommandationManagerApi->actionCricCrocCheckCodeReco((string) Tools::getValue('code'));
                    break;

                case 'delete':
                    $ret = $this->actionDelete();
                    break;

                case 'get':
                    $ret = $this->actionGet();
                    break;

                case 'list':
                    $ret = $this->actionList();
                    break;

                case 'update':
                    $ret = $this->actionUpdate();
                    break;

                case 'search_sac_detail':
                    $ret = $this->actionSearchSacDetail();
                    break;

                case 'associate':
                    $ret = $this->actionAssociate();
                    break;

                case 'sacsListAvailable':
                    $ret = $this->actionSacListAvailable();
                    break;

                case 'updateAnimalSac':
                    $ret = $this->updateAnimalSac();
                    break;

                default:
                    $ret = [];
            }

            echo json_encode($ret, JSON_PRETTY_PRINT);
            exit();
        } catch (PrestaShopDatabaseException $e) {
            header('HTTP/1.0 500 Internal Server Error');
            echo json_encode(['error' => $e->getMessage()]);
            exit();
        } catch (PrestaShopException $e) {
            header('HTTP/1.0 500 Internal Server Error');
            echo json_encode(['error' => $e->getMessage()]);
            exit();
        } catch (Exception $e) {
            $statusCode ? http_response_code($statusCode) : http_response_code(500);
            echo json_encode(['error' => $e->getMessage()]);
            exit();
        }
    }
}
