<?php

namespace AutoAdmin\Bundle\Content\Command;

use AutoAdmin\Bundle\Content\Entity\Site;
use AutoAdmin\Bundle\Content\Entity\StaticText;
use AutoAdmin\Bundle\Content\Site\SiteContextHelper;
use AutoAdmin\Bundle\Content\Translation\EntityTranslator;
use Doctrine\ORM\EntityManagerInterface;
use Exception;
use SplFileObject;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Finder\Finder;

#[AsCommand(
    name: 'aa:static-translation:generate',
    description: 'Read static translations from templates tags, and create database entries when possible',
    hidden: false
)]
class StaticTranslationCommand extends Command
{
    public function __construct(
        protected EntityManagerInterface $em,
        protected EntityTranslator $entityTranslator,
        protected SiteContextHelper $contextHelper,
        protected string $projectDir,
    ) {
        parent::__construct();
    }

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

        $newKeys = [];
        $allKeys = [];
        $existingKeys = [];
        $aggregatedKeys = [];


        // first read templates to find all aa_trans tags
        // this step could later be integrated with site-template relation, to have different keys for different sites
        $templateRoot = $this->projectDir.'/templates';
        $stRepo = $this->em->getRepository(StaticText::class);

        // here, we want a simple key adding possibilities will complicate parsing,
        // so: only lowercase+numbers and only underscore+dot as usable separator
        $pattern = "/(\\'|\\\")([a-z0-9_\.]+)(\\'|\\\")\|aa_trans(\(\{(.+?)?\}\,\ ?(\\'|\\\")([a-z0-9_\.]+)(\\'|\\\")\))?/";

        $io->writeln('Reading templates for keys...');

        $fs = new Finder();
        $fs->in($templateRoot)->files()->sortByName();

        foreach ($fs as $file) {
            preg_match_all($pattern, file_get_contents($file->getRealPath()), $matches);

            if (!empty($matches[2])) {
                $keys = $matches[2];
                $domains = $matches[7];

                foreach ($keys as $k => $key) {
                    $allKeys[] = $key;
                    $exists = $stRepo->findBy(['key' => $key]);
                    if (empty($exists)) {
                        $newKeys[] = $key.':'.($domains[$k] ?: 'messages');
                    } else {
                        $existingKeys[] = $key;
                    }
                }
            }
        }

        $newUniqueKeys = array_unique($newKeys);

        // second generate associations with site and locales for db migration
        try {
            $siteRepo = $this->em->getRepository(Site::class);
            foreach ($siteRepo->findBy(['enabled' => true]) as $site) {
                $siteConfigs = $this->contextHelper->getSiteConfigs($site);
                foreach ($siteConfigs as $siteConfig) {
                    $this->contextHelper->loadSiteConfig($siteConfig);
                    $io->note(
                        sprintf(
                            'Running for site: %s and locale: %s',
                            $siteConfig->getSite()->getSlug(),
                            $siteConfig->getLocale()->getCode()
                        )
                    );

                    $io->writeln('Generating aggregates for SiteConfig...');

                    // create aggregates using complete data
                    foreach ($newUniqueKeys as $newKey) {
                        list($key, $domain) = explode(':', $newKey);
                        $aggregate = implode(
                            ':',
                            [
                                $key,
                                $siteConfig->getSite()->getId(),
                                $siteConfig->getLocale()->getId(),
                                $domain,
                            ]
                        );

                        $aggregatedKeys[] = $aggregate;
                    }
                }
            }
        } catch (Exception $exception) {
            $output->writeln(sprintf('<error>%s</error>', $exception->getMessage()));

            return Command::FAILURE;
        }


        // Now that we have keys, generate migration file
        if (!empty($aggregatedKeys)) {
            $migrationGenerate = new ArrayInput(['command' => 'doctrine:migration:generate',]);
            $returnCode = $this->getApplication()->doRun($migrationGenerate, $output);

            if ($returnCode !== 0) {
                $output->writeln('Error while generating migration');

                return Command::FAILURE;
            }
        }

        $migrationRoot = $this->projectDir.'/migrations';
        $lastMigrationIter = (new Finder())->files()->in($migrationRoot)->sortByName()->reverseSorting();
        $lastMigrationPath = array_keys(\iterator_to_array($lastMigrationIter))[0];

        $io->info('New keys found: '.count($newKeys).' (unique: '.count($newUniqueKeys).')');
        $io->writeln(implode(', ', $newKeys));

        $io->info('Already existing keys found: '.count($existingKeys));

        $io->info('Total keys encountered: '.count($allKeys));

        if (!empty($aggregatedKeys)) {
            $io->writeln('Generating migration file...');

            $addedContent = '';
            foreach ($aggregatedKeys as $aggregated) {
                $addedContent .= "\n".str_repeat('    ', 3).sprintf('\'%s\',', $aggregated);
            }

            $file = new SplFileObject($lastMigrationPath, 'r+');

            $functions = sprintf($this->getFunctionTemplate(), $addedContent)."\n";

            // add static keys array func
            $this->addToLine($file, $this->getLastLineNum($file) - 1, $functions);

            // add up and down calls, for now using line numbers, could be improved by using comment added when generating migration
            $this->addToLine($file, 22, '        $this->addStaticTranslations();');
            $this->addToLine($file, 29, '        $this->removeStaticTranslations();');

            $io->success('Migration generated here: /migrations/'.basename($lastMigrationPath));
        } else {
            $io->success('No migration needed.');
        }

        return Command::SUCCESS;
    }

    private function getLastLineNum(SplFileObject $file): int
    {
        return count(file($file->getRealPath(), FILE_IGNORE_NEW_LINES));
    }

    private function addToLine(SplFileObject $file, int $line, string $content): void
    {
        $fileArray = file($file->getRealPath(), FILE_IGNORE_NEW_LINES);
        array_splice($fileArray, $line, 0, $content);

        // replace line and add content
        $file->seek($line);
        $line = $file->fread(9999);
        $file->fwrite($line.$content);

        file_put_contents($file->getRealPath(), implode(PHP_EOL, $fileArray));
    }

    private function getFunctionTemplate(): string
    {
        $functionTemplate = <<<TEMPLATE

    private function addStaticTranslations(): void
    {
        foreach (\$this->getStaticTranslationKeys() as \$aggrKey) {
            list(\$key, \$site, \$locale, \$domain) = explode(':', \$aggrKey);
            \$this->addSql('INSERT IGNORE INTO static_text (trans_key, site_id, domain, created_at, updated_at) VALUES (\''.\$key.'\', \''.\$site.'\', \''.\$domain.'\', NOW(), NOW());');
            // translations
            \$this->addSql('SELECT @translatableid := COALESCE(id, 1) FROM static_text WHERE trans_key = \''.\$key.'\';');
            \$this->addSql('INSERT INTO static_text_translation (translatable_id, locale_id, created_at, updated_at ) VALUES (@translatableid, \''.\$locale.'\', NOW(), NOW());');
        }
    }

    private function removeStaticTranslations(): void
    {
        foreach (\$this->getStaticTranslationKeys() as \$aggrKey) {
            list(\$key, \$site, \$locale) = explode(':', \$aggrKey);
            \$this->addSql('DELETE FROM static_text WHERE trans_key = \''.\$key.'\';');
        }
    }

    private function getStaticTranslationKeys(): array
    {
        return [%s
        ];
    }
TEMPLATE;

        return $functionTemplate;
    }
}
