<?php

declare(strict_types=1);

/*
 * This file is part of the VitiCore 2.
 *
 * (c) CP Creation <web@cpcreation.fr>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace CpCreation\VitiCore\Bridge\Symfony\Bundle\Provider\Carrier;

use CpCreation\VitiCore\Bridge\Symfony\Bundle\Filesystem\Filesystem;
use CpCreation\VitiCore\Shop\Model\ShippingAuto;
use CpCreation\VitiCore\Shop\Model\ShippingConfig;
use Doctrine\ORM\EntityManagerInterface;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
use Symfony\Component\HttpFoundation\RequestStack;

final class GeninProvider implements CarrierProviderInterface
{
    public const TYPE = 'genin';
    private const CSV_LENGTH = 1000;

    /**
     * @var array<string,string>
     */
    private array $pal = [
        '1/4 PAL*' => '0 à 114',
        '1/2 PAL*' => '114 à 228',
        '1 PAL' => '228 à 480',
        '2 PAL' => '480 à 960',
        '3 PAL' => '960 à 1440',
    ];

    public function __construct(
        private RequestStack $requestStack,
        private Filesystem $filesystem,
        private EntityManagerInterface $em,
    ) {
    }

    /**
     * @throws \PhpOffice\PhpSpreadsheet\Reader\Exception
     * @throws \PhpOffice\PhpSpreadsheet\Writer\Exception
     */
    public function setData(ShippingConfig $config): void
    {
        $request = $this->requestStack->getCurrentRequest();
        if (null === $request) {
            return;
        }

        $files = $request->files->get('shipping_config');
        if (!\is_array($files)) {
            return;
        }

        $uploadedFile = $files['file']['file'] ?? null;
        if (null === $uploadedFile) {
            return;
        }

        $reader = new Xlsx();
        $reader->setReadDataOnly(true);

        $xlsxPath = $this->filesystem->absolutePath('file', $config->getFile()->getPath());
        $spreadsheet = $reader->load($xlsxPath);

        $writer = new Csv($spreadsheet);
        $csvPath = $this->filesystem->absolutePath('file', 'genin.csv');
        $writer->save($csvPath);

        $handle = fopen($csvPath, 'r');
        if (false === $handle) {
            return;
        }

        $classMetaData = $this->em->getClassMetadata(ShippingAuto::class);
        $connection = $this->em->getConnection();
        $dbPlatform = $connection->getDatabasePlatform();

        $connection->beginTransaction();

        try {
            $connection->executeStatement('SET FOREIGN_KEY_CHECKS=0');
            $connection->executeStatement($dbPlatform->getTruncateTableSql($classMetaData->getTableName()));
            $connection->executeStatement('SET FOREIGN_KEY_CHECKS=1');

            /** @var array<string, array{of:int, at:int}> $shippingQtyCollection */
            $shippingQtyCollection = [];
            $keysPal = array_keys($this->pal);

            while (($data = fgetcsv($handle, self::CSV_LENGTH, ',')) !== false) {
                $this->hydrateShippingQuantities($data, $shippingQtyCollection);

                $firstCell = (string) ($data[0] ?? '');

                if (!$this->isDepartmentLine($firstCell)) {
                    continue;
                }

                $departments = array_chunk($data, 7);

                foreach ($departments as $department) {
                    if ('' === (string) ($department[0] ?? '')) {
                        continue;
                    }

                    $county = mb_substr((string) $department[0], 0, 2);

                    foreach ($this->pal as $key => $range) {
                        unset($range);

                        if (!isset($shippingQtyCollection[$key])) {
                            continue;
                        }

                        $shipping = new ShippingAuto();
                        $shipping->setCountry('FR');
                        $shipping->setCounty($county);
                        $shipping->setQtyOf($shippingQtyCollection[$key]['of']);
                        $shipping->setQtyAt($shippingQtyCollection[$key]['at']);

                        $colIndex = array_search($key, $keysPal, true);
                        $raw = false !== $colIndex ? ($department[$colIndex + 2] ?? 0) : 0;
                        $shipping->setPackagePrice((int) ((float) $raw * 100));

                        $this->em->persist($shipping);
                    }
                }
            }

            $this->em->flush();
            fclose($handle);
            $connection->commit();
        } catch (\Throwable $e) {
            fclose($handle);
            $connection->rollBack();

            throw $e;
        }
    }

    public function getCarrier(): string
    {
        return self::TYPE;
    }

    /**
     * @param array<int, string|null>              $data
     * @param array<string, array{of:int, at:int}> $shippingQtyCollection
     */
    private function hydrateShippingQuantities(array $data, array &$shippingQtyCollection): void
    {
        $num = \count($data);

        for ($c = 0; $c < $num; ++$c) {
            if (($data[$c] ?? null) !== 'Délai') {
                continue;
            }

            for ($cc = 1; $cc < $num; ++$cc) {
                $value = $data[$cc] ?? '';

                if ('' === $value || null === $value) {
                    continue;
                }

                foreach ($this->pal as $key => $pal) {
                    if ($key !== $value) {
                        continue;
                    }

                    $explode = explode('à', $pal);
                    $shippingQtyCollection[$value]['of'] = (int) mb_trim($explode[0]);
                    $shippingQtyCollection[$value]['at'] = (int) mb_trim($explode[1]);
                }
            }

            break;
        }
    }

    private function isDepartmentLine(string $firstCell): bool
    {
        if ('' === $firstCell) {
            return false;
        }

        if ('51100' === $firstCell || '0' === $firstCell) {
            return false;
        }

        $firstTwo = mb_substr($firstCell, 0, 2);

        return is_numeric($firstTwo) && is_numeric($firstCell);
    }
}
