<?php

namespace Myvetshop\Module\Clinique\Command\Import;

use Myvetshop\Module\Clinique\Import\ImportFile\DispatchedCustomerDataFactory;
use Myvetshop\Module\Clinique\Import\ImportFile\ImportDispatcher;
use Myvetshop\Module\Clinique\Import\ImportFile\ImportFileParser;
use Myvetshop\Module\Clinique\Import\Kalivet\Clinic\ClinicImporter;
use Myvetshop\Module\Clinique\Import\Kalivet\Clinic\ClinicImportFileProvider;
use Myvetshop\Module\Clinique\Import\Kalivet\ImportFile\ImportFileProvider;
use Myvetshop\Module\Clinique\Import\Kalivet\Model\ClinicModel;
use Myvetshop\Module\Clinique\Import\Kalivet\Model\CustomerModel;
use Myvetshop\Module\Clinique\Import\Kalivet\Sync\KalivetDataSyncer;
use Myvetshop\Module\Clinique\Import\Sync\SyncStatistics;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class KalivetImportCommand extends Command
{
    protected ClinicImportFileProvider $clinicImportFileProvider;
    protected ClinicImporter $clinicImporter;
    protected KalivetDataSyncer $kalivetDataSyncer;
    protected DispatchedCustomerDataFactory $customerDataFactory;
    protected ImportDispatcher $importDispatcher;
    protected ImportFileProvider $importFileProvider;

    protected function configure(): void
    {
        $this
            ->addOption('clinics', null, InputOption::VALUE_NONE, 'Importer les cliniques')
            ->addOption('customers', null, InputOption::VALUE_NONE, 'Importer les clients (implique l\'import des cliniques)')
            ->addOption('clinic', null, InputOption::VALUE_REQUIRED, 'Importer uniquement le code privilège sélectionné')
            ->addOption('all', null, InputOption::VALUE_NONE, 'Importer toutes les cliniques');
    }

    /**
     * @return array<string, \Clinique>
     */
    protected function importClinics(SymfonyStyle $output, ?string $code): array
    {
        $ret = [];

        $output->title('Syncinc clinics...');

        $clinics = $this->clinicImportFileProvider->getClinics($code);
        $progressBar = new ProgressBar($output, \count($clinics));
        foreach ($clinics as $i => $clinic) {
            $progressBar->setProgress($i);
            $ret[$clinic->codeAlcyonKalivet] = $this->clinicImporter->import($clinic);

            // Clear local cache
            \Cache::clear();
        }
        $progressBar->finish();

        return $ret;
    }

    public function __construct(
        ClinicImportFileProvider $clinicImportFileProvider,
        ClinicImporter $clinicImporter,
        KalivetDataSyncer $dataSyncer,
        DispatchedCustomerDataFactory $customerDataFactory,
        ImportDispatcher $importDispatcher,
        ImportFileProvider $importFileProvider
    ) {
        $this->clinicImportFileProvider = $clinicImportFileProvider;
        $this->clinicImporter = $clinicImporter;
        $this->kalivetDataSyncer = $dataSyncer;
        $this->customerDataFactory = $customerDataFactory;
        $this->importDispatcher = $importDispatcher;
        $this->importFileProvider = $importFileProvider;

        parent::__construct('app:import:kalivet');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $styledOutput = new SymfonyStyle($input, $output);

        if (!$input->getOption('all') && !$input->getOption('clinic')) {
            $styledOutput->error('You must specify --all or --clinic=ABC123 to import all or only one clinic');

            return 1;
        }

        // Init Context with a new Cart
        $context = \Context::getContext();
        \assert($context instanceof \Context);
        if (!$context->cart) {
            $context->cart = new \Cart();
        }
        // Workaround pour PrestaShop
        // @phpstan-ignore-next-line
        $context->controller = (object) [
            'controller_type' => 'cron',
            'php_self' => 'delete_clinique',
        ];

        // Deactivate caching
        \Module::getInstanceByName('myvetshopclinique');
        \Cache::setInstanceForTesting(new \NoOpCache());
        \Db::getInstance()->disableCache();

        /** @var string|null $code */
        $code = $input->getOption('clinic') ?: null;

        $clinics = $this->clinicImportFileProvider->getClinics($code);
        $kalivetCodes = \array_map(
            function (ClinicModel $clinicModel) { return $clinicModel->codeAlcyonKalivet; },
            $clinics
        );

        // Download export
        $styledOutput->title('Downloading export from Kalivet...');
        if ($this->importFileProvider->isCached()) {
            $styledOutput->text('Export cached, no download required');
        } else {
            $this->importFileProvider->download($kalivetCodes);
        }

        $importFile = $this->importFileProvider->open();

        // Count lines of the import file
        $lineCount = 0;
        while (!\feof($importFile)) {
            // Load 1Mb of file at each loop
            $line = \fgets($importFile, 1048576);
            if (false === $line) {
                break;
            }
            $lineCount += \substr_count($line, "\n");
        }

        \rewind($importFile);
        $styledOutput->text($lineCount . ' lines to process');

        // Pre-process export file
        $styledOutput->title('Pre-processing export file...');
        $parser = new ImportFileParser($importFile);
        $this->importDispatcher->prepare();

        $progressBar = new ProgressBar($styledOutput, $lineCount);
        $currentSection = null;
        foreach ($parser as $lineNumber => $value) {
            $newSection = $parser->getSection();
            if ($newSection !== $currentSection) {
                $styledOutput->section($newSection);
                $currentSection = $newSection;
            }

            $progressBar->setProgress($lineNumber);

            if (\is_string($currentSection)) {
                $this->importDispatcher->dispatchLine($currentSection, $value);
            }
        }

        $progressBar->finish();
        \fclose($importFile);

        $importClinics = $input->getOption('clinics');
        $importCustomers = $input->getOption('customers');

        // Sync clinics
        if ($importClinics || $importCustomers) {
            $clinicMap = $this->importClinics($styledOutput, $code);
        }

        \Cache::clear();

        // Process per-customer import
        if ($importCustomers && isset($clinicMap)) {
            $folders = $this->importDispatcher->listFolders();
            $styledOutput->title('Processing customer data');
            $progressBar = new ProgressBar($styledOutput, \count($folders));

            $statistics = new SyncStatistics();
            foreach ($folders as $i => $folder) {
                $progressBar->setProgress($i);

                $customerData = $this->customerDataFactory->create($folder);

                try {
                    $this->kalivetDataSyncer->sync($clinicMap, $customerData, $statistics);
                } catch (\Exception $e) {
                    $styledOutput->error('Error processing ' . $customerData->getCustomer(CustomerModel::class)->email . ' : ' . $e->getMessage());
                    $styledOutput->error($e->getFile() . '#' . $e->getLine());
                    $styledOutput->error($e->getTraceAsString());
                }
            }

            $progressBar->finish();

            $styledOutput->text($statistics->nbCustomersCreated . ' customers created');
            $styledOutput->text($statistics->nbCustomersUpdated . ' customers updated');
            $styledOutput->text($statistics->nbAddressesCreated . ' addresses created');
            $styledOutput->text($statistics->nbAddressesUpdated . ' addresses updated');
            $styledOutput->text($statistics->nbOrdersCreated . ' orders created');
            $styledOutput->text($statistics->nbOrdersUpdated . ' orders updated');
        }

        // Nettoyage
        $styledOutput->title('Post-import cleanup...');
        $this->importFileProvider->purge();
        $this->importDispatcher->purge();

        return 0;
    }
}
