<?php

declare(strict_types=1);

require_once __DIR__ . \DIRECTORY_SEPARATOR . 'ExportProduct.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . 'Clinique.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'CsvExporter.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'ExportAppVeto' . \DIRECTORY_SEPARATOR . 'ExportGenerator.php';
require_once __DIR__ . \DIRECTORY_SEPARATOR . '..' . \DIRECTORY_SEPARATOR . 'Service' . \DIRECTORY_SEPARATOR . 'ExportAppVeto' . \DIRECTORY_SEPARATOR . 'ExportSender.php';

class WebserviceSpecificManagementExportProducts implements WebserviceSpecificManagementInterface
{
    /** @var WebserviceOutputBuilderCore|null */
    protected $objOutput;

    /** @var mixed */
    protected $output;

    /** @var WebserviceRequestCore|null */
    protected $wsObject;

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     *
     * @throw Exception
     */
    protected function loadExportProduct(int $exportProductId): ExportProduct
    {
        \assert(null !== $this->wsObject);
        \assert(\method_exists($this->wsObject, 'getIdEmployee'));

        $exportProduct = new ExportProduct($exportProductId);

        $clinique = new Clinique($exportProduct->id_myvetshop_clinique);

        if ($exportProduct->deleted) {
            throw new Exception('Export deleted');
        }

        if ((int) $clinique->id_employee !== (int) $this->wsObject->getIdEmployee()) {
            throw new Exception('Access denied');
        }

        return $exportProduct;
    }

    /**
     * Chargement des informations JSON depuis la requête.
     * Seuls les champs autorisés sont retournés.
     *
     * @return array<string, mixed> Le JSON parsé et filrés (aucun champ non autorisé)
     *
     * @throws Exception
     */
    protected static function loadJsonFromRequest(): array
    {
        $contentType = $_SERVER['CONTENT_TYPE'] ?? null;

        if ('application/json' !== $contentType) {
            throw new Exception('Content-Type invalide');
        }

        $content = \file_get_contents('php://input');

        if (!$content) {
            throw new Exception('Une erreur s\'est produite lors de la récupération du contenu de la requête');
        }

        $json = \json_decode($content, true);
        if (!\is_array($json)) {
            $json = [];
        }
        // Liste les champs autorisés (tous les champs + id)
        $allowedFields = \array_merge(
            ['id', 'execute'],
            \array_keys(ExportProduct::$definition['fields'])
        );

        $ret = \array_filter(
            $json,
            fn ($key) => \in_array($key, $allowedFields),
            \ARRAY_FILTER_USE_KEY
        );
        // Champ spécial qui ne peut pas être édité : id_myvetshop_clinique
        if (isset($ret['id_myvetshop_clinique'])) {
            unset($ret['id_myvetshop_clinique']);
        }

        return $ret;
    }

    /**
     * @param array<string, mixed> $json
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected static function updateEntityFromJson(ExportProduct $exportProduct, array $json): void
    {
        foreach (ExportProduct::$definition['fields'] as $key => $field) {
            if (isset($json[$key])) {
                switch ($key) {
                    case 'products':
                        if (\is_array($json[$key])) {
                            $exportProduct->setWsProducts($json[$key]);
                        }
                        break;

                    default:
                        $exportProduct->$key = $json[$key];
                }
            }
        }
        // Champs spécifiques
        $unsavedFields = ['delete', 'execute'];
        foreach ($unsavedFields as $field) {
            if (isset($json[$field])) {
                $exportProduct->$field = $json[$field]; /* @phpstan-ignore-line */
            }
        }
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function handleList(): void
    {
        \assert(null !== $this->objOutput);
        \assert(null !== $this->wsObject);
        \assert(\method_exists($this->wsObject, 'getIdEmployee'));

        $query = 'SELECT e.* FROM `' . _DB_PREFIX_ . ExportProduct::TABLE . '` e';

        if ($this->wsObject instanceof AppWebserviceRequest) {
            $query .= ' INNER JOIN `' . _DB_PREFIX_ . Clinique::TABLE . '` c ON c.id_myvetshop_clinique = e.id_myvetshop_clinique'
                . ' WHERE c.id_employee = ' . (int) $this->wsObject->getIdEmployee() . ' AND e.deleted = 0';
        }

        $result = \Db::getInstance(false)
            ->executeS($query);

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

        $exports = \ObjectModel::hydrateCollection(
            ExportProduct::class, $result
        );

        $exports['empty'] = new ExportProduct();
        $this->output .= $this->objOutput->getContent(
            $exports,
            null,
            'full',
            $this->wsObject->depth,
            WebserviceOutputBuilder::VIEW_LIST,
            false
        );
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function handleGet(int $exportProductId): void
    {
        \assert(null !== $this->objOutput);
        \assert(null !== $this->wsObject);

        $exportProduct = $this->loadExportProduct($exportProductId);

        $exports = [
            $exportProduct,
            'empty' => new ExportProduct(),
        ];
        $this->objOutput->getContent(
            $exports,
            null,
            'full',
            $this->wsObject->depth,
            WebserviceOutputBuilder::VIEW_DETAILS,
            false
        );
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function handleCreate(): void
    {
        \assert(null !== $this->objOutput);
        \assert(null !== $this->wsObject);
        \assert(\method_exists($this->wsObject, 'getIdEmployee'));

        $clinique = Clinique::getCliniqueByEmployeeId((int) $this->wsObject->getIdEmployee());

        if (!$clinique || !$clinique->id) {
            throw new Exception('Clinique inconnue');
        }

        $json = self::loadJsonFromRequest();
        $exportProduct = new ExportProduct();
        $exportProduct->id_myvetshop_clinique = $clinique->id;

        self::updateEntityFromJson($exportProduct, $json);

        // Sauvegarde le nouvel export
        $exportProduct->save();

        // Retourne l'export créé
        $exports = [
            $exportProduct,
            'empty' => new ExportProduct(),
        ];
        $this->objOutput->getContent(
            $exports,
            null,
            'full',
            $this->wsObject->depth,
            WebserviceOutputBuilder::VIEW_DETAILS,
            false
        );
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function handleUpdate(int $exportProductId): void
    {
        \assert(null !== $this->objOutput);
        \assert(null !== $this->wsObject);
        \assert(\method_exists($this->wsObject, 'getIdEmployee'));

        $exportProduct = $this->loadExportProduct($exportProductId);

        $json = self::loadJsonFromRequest();
        self::updateEntityFromJson($exportProduct, $json);

        // Sauvegarde le nouvel export
        $exportProduct->save();
        // Exécution de l'export
        if ($exportProduct->execute) {
            $exportGenerator = new ExportGenerator();
            $exporter = $exportGenerator->generateExport($exportProduct);
            $employee = new Employee($this->wsObject->getIdEmployee());
            $exportSender = new ExportSender();
            $email = $employee->email;

            $devMode = Configuration::get('MVS_DEV_MODE');
            if ($devMode) {
                $email = 'contact@btoweb.fr';
            }

            $exportSender->sendExport($exportProduct, (string) $exporter->getContent(), $email);
        }

        // Retourne l'export créé
        $exports = [
            $exportProduct,
            'empty' => new ExportProduct(),
        ];

        $this->objOutput->getContent(
            $exports,
            null,
            'full',
            $this->wsObject->depth,
            WebserviceOutputBuilder::VIEW_DETAILS,
            false
        );
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    protected function handleDelete(int $exportProductId): void
    {
        $export = self::loadExportProduct($exportProductId);
        $export->deleted = true;
        $export->update();
    }

    public function setObjectOutput(WebserviceOutputBuilderCore $obj): self
    {
        $this->objOutput = $obj;

        return $this;
    }

    public function getObjectOutput(): ?WebserviceOutputBuilderCore
    {
        return $this->objOutput;
    }

    public function setWsObject(WebserviceRequestCore $obj): self
    {
        $this->wsObject = $obj;

        return $this;
    }

    public function getWsObject(): ?WebserviceRequestCore
    {
        return $this->wsObject;
    }

    public function manage(): void
    {
        try {
            switch ($_SERVER['REQUEST_METHOD']) {
                case 'GET':
                    if (isset($this->wsObject->urlSegment[1])) {
                        $this->handleGet((int) $this->wsObject->urlSegment[1]);
                    } else {
                        $this->handleList();
                    }
                    break;

                case 'POST':
                    $this->handleCreate();
                    break;

                case 'PUT':
                    if (!isset($this->wsObject->urlSegment[1])) {
                        throw new Exception('ID not specified');
                    }
                    $this->handleUpdate((int) $this->wsObject->urlSegment[1]);
                    break;

                case 'DELETE':
                    if (!isset($this->wsObject->urlSegment[1])) {
                        throw new Exception('ID not specified');
                    }
                    $this->handleDelete((int) $this->wsObject->urlSegment[1]);
                    break;
            }
        } catch (Exception $exception) {
            \header('Content-Type: application/json');
            \http_response_code(500);
            echo \json_encode(['error' => $exception->getMessage()]);
            exit;
        }
    }

    /**
     * This must be return an array with specific values as WebserviceRequest expects.
     *
     * @return array<mixed>|string
     */
    public function getContent()
    {
        if (!$this->objOutput) {
            return [];
        }

        return $this->objOutput->getObjectRender()->overrideContent($this->output);
    }
}
