<?php

declare(strict_types=1);

use Myvetshop\Module\Clinique\Accounting\Export\AccountingExporter;

require_once __DIR__
    . \DIRECTORY_SEPARATOR . '..'
    . \DIRECTORY_SEPARATOR . '..'
    . \DIRECTORY_SEPARATOR . 'classes'
    . \DIRECTORY_SEPARATOR . 'OrderSlipExporter.php';

class AdminMyVetShopExportComptableController extends ModuleAdminController
{
    /**
     * @return array{startDate: \DateTimeImmutable, endDate: \DateTimeImmutable}|null
     */
    protected function getStartEndDates(): ?array
    {
        $start = \strval(\Tools::getValue('start'));
        $end = \strval(\Tools::getValue('end'));

        if (!$start || !$end) {
            return null;
        }

        $startDate = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $start . ' 00:00:00');
        $endDate = \DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $end . ' 00:00:00');

        if (!$startDate) {
            throw new Exception('Une erreur s\'est produite lors de l\'instanciation de la date de début');
        }

        if (!$endDate) {
            throw new Exception('Une erreur s\'est produite lors de l\'instanciation de la date de fin');
        }

        return [
            'startDate' => $startDate,
            'endDate' => $endDate,
        ];
    }

    /**
     * Créé des chunks de 1 semaine maximum à partir de $start jusqu'à $end.
     *
     * @return array<int,array<int, bool|DateTimeImmutable>>
     */
    protected function chunkize(DateTimeImmutable $start, DateTimeImmutable $end): array
    {
        $chunks = [];

        $chunkStart = $start;
        do {
            $chunkEnd = $chunkStart->add(new \DateInterval('P7D'));
            if ($chunkEnd > $end) {
                $chunkEnd = $end;
            }

            // Ajout du morceau de 1 semaine
            $chunks[] = [
                $chunkStart,
                $chunkEnd,
            ];

            // On déplace le prochain chunk
            $chunkStart = $chunkEnd;
        } while ($chunkStart < $end);

        return $chunks;
    }

    /**
     * Récupère toutes les commandes ayant une facture ou un avoir compris dans l'interval indiqué.
     *
     * @return OrderInvoice[]
     */
    protected function getInvoicesForChunk(DateTimeInterface $start, DateTimeInterface $end): array
    {
        $result = Db::getInstance(false)
            ->executeS(
                'SELECT `oi`.*'
                . ' FROM `ps_orders` o'
                . ' INNER JOIN `ps_order_invoice` oi ON `oi`.`id_order` = `o`.`id_order`'
                . ' LEFT JOIN `ps_order_slip` os ON `os`.`id_order` = `o`.`id_order`'
                . ' WHERE (`o`.`invoice_date` >= "' . $start->format('Y-m-d H:i:s') . '" AND `o`.`invoice_date` < "' . $end->format('Y-m-d H:i:s') . '")'
                . ' OR (`os`.`date_add` >= "' . $start->format('Y-m-d H:i:s') . '" AND `os`.`date_add` < "' . $end->format('Y-m-d H:i:s') . '")'
            );

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

        return ObjectModel::hydrateCollection(
            OrderInvoice::class, $result
        );
    }

    /**
     * Récupère toutes les commandes ayant une facture ou un avoir compris dans l'interval indiqué.
     *
     * @return OrderSlip[]
     */
    protected function getOrderSlipForChunk(DateTimeInterface $start, DateTimeInterface $end): array
    {
        $result = Db::getInstance(false)
            ->executeS(
                'SELECT `os`.*'
                . ' FROM `ps_order_slip` os'
                . ' WHERE (`os`.`date_add` >= "' . $start->format('Y-m-d H:i:s') . '" AND `os`.`date_add` < "' . $end->format('Y-m-d H:i:s') . '")'
            );

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

        return ObjectModel::hydrateCollection(
            OrderSlip::class, $result
        );
    }

    public function __construct()
    {
        parent::__construct();

        $this->bootstrap = true;

        // Module::getInstanceByName('myvetshopclinique');
    }

    /**
     * @return array<string, mixed>
     *
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function getTemplateViewVars(): array
    {
        $type = \strval(\Tools::getValue('type', 'comptable'));
        $infosExports = null;
        $dates = $this->getStartEndDates();

        if ($dates) {
            $chunks = $this->chunkize($dates['startDate'], $dates['endDate']);

            if ('comptable' == $type) {
                $container = $this->getContainer();
                \assert(null !== $container);

                /** @var AccountingExporter $exporter */
                $exporter = $container->get(AccountingExporter::class);

                $statsExport = $exporter->getStats($dates['startDate'], $dates['endDate']);
            } elseif ('remboursement' == $type) {
                $infosExports = [
                    'nb_remboursements' => 0,
                    'nb_errors' => 0,
                    'nb_lignes' => 0,
                ];

                foreach ($chunks as $chunk) {
                    if (!isset($chunk[0]) || !isset($chunk[1]) || !$chunk[0] instanceof DateTimeInterface || !$chunk[1] instanceof DateTimeInterface) {
                        throw new Exception('Le format des données n\'est pas valide');
                    }

                    $invoices = $this->getOrderSlipForChunk($chunk[0], $chunk[1]);
                    $exports = array_map(
                        fn ($orderSlip) => new OrderSlipExporter($orderSlip), $invoices
                    );

                    $infosExports = array_reduce(
                        $exports,
                        function ($infosExports, $export) {
                            /* @var OrderSlipExporter $export */
                            ++$infosExports['nb_remboursements'];

                            try {
                                $lignes = $export->getLignesComptables();
                                $infosExports['nb_lignes'] += count($lignes);
                            } catch (Exception $e) {
                                ++$infosExports['nb_errors'];
                            }

                            return $infosExports;
                        },
                        $infosExports
                    );
                }
            }
        }

        return [
            'start' => $dates ? $dates['startDate']->format('Y-m-d H:i:s') : '',
            'end' => $dates ? $dates['endDate']->format('Y-m-d H:i:s') : '',
            'type' => $type,
            'info_exports' => $infosExports,
        ];
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function renderList(): string
    {
        $helper = new HelperView();
        $this->setHelperDisplay($helper);
        $helper->tpl_vars = $this->getTemplateViewVars();
        if (null !== $this->base_tpl_view) {
            $helper->base_tpl = $this->base_tpl_view;
        }
        $view = $helper->generateView();

        return $view;
    }

    /**
     * @throws PrestaShopDatabaseException
     * @throws PrestaShopException
     */
    public function processDownloadComptable(): void
    {
        $dates = $this->getStartEndDates();
        if (!$dates) {
            throw new \Exception('No dates found');
        }

        $container = $this->getContainer();
        \assert(null !== $container);

        /** @var AccountingExporter $exporter */
        $exporter = $container->get(AccountingExporter::class);

        $stream = $exporter->getCsv($dates['startDate'], $dates['endDate']);

        \header('Content-Type: text/csv');
        \header(
            'Content-Disposition: attachment; filename="export-comptable-'
            . $dates['startDate']->format('Y-m-d') . '-' . $dates['endDate']->format('Y-m-d') . '.csv"'
        );
        \fpassthru($stream);
        \fclose($stream);

        exit(0);
    }

    /**
     * @throws Exception
     */
    public function processDownloadRemboursement(): void
    {
        $start = Tools::getValue('start');
        $end = Tools::getValue('end');

        $startDate = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $start . ' 00:00:00');
        $endDate = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $end . ' 00:00:00');

        if (!$startDate) {
            throw new Exception('Une erreur s\'est produite lors de l\'instanciation de la date de début');
        }

        if (!$endDate) {
            throw new Exception('Une erreur s\'est produite lors de l\'instanciation de la date de fin');
        }

        $endDate = $endDate->add(new DateInterval('P1D'));

        $csv = fopen('php://temp', 'w');
        if (!$csv) {
            var_dump('Erreur générale');
            exit;
        }
        fputcsv($csv, ['numero_avoir', 'date_avoir', 'id_commande', 'date_facture', 'clinique', 'centrale', 'entrepot', 'produits_ht', 'port_ht', 'total_ttc'], ';');

        $chunks = $this->chunkize($startDate, $endDate);

        foreach ($chunks as $chunk) {
            if (!isset($chunk[0]) || !isset($chunk[1]) || !$chunk[0] instanceof DateTimeInterface || !$chunk[1] instanceof DateTimeInterface) {
                throw new Exception('Le format des données n\'est pas valide');
            }

            $invoices = $this->getOrderSlipForChunk($chunk[0], $chunk[1]);
            $exports = array_map(
                fn ($orderSlip) => new OrderSlipExporter($orderSlip), $invoices
            );

            foreach ($exports as $export) {
                try {
                    $lignes = $export->getLignesComptables();

                    foreach ($lignes as $ligne) {
                        fputcsv($csv, $ligne, ';');
                    }
                } catch (Exception $e) {
                    // Inhibiteur d'erreur
                }
            }
        }

        // Rembobine le flux
        rewind($csv);

        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename="export-remboursement-' . $startDate->format('Y-m-d') . '-' . $endDate->format('Y-m-d') . '.csv"');
        fpassthru($csv);
        fclose($csv);

        exit(0);
    }
}
