<?php
declare(strict_types=1);

namespace Autogedal\EanTools\Model\Tool;

use Autogedal\EanTools\Model\EanColumnParser;
use Autogedal\EanTools\Model\Normalizer;
use Autogedal\EanTools\Model\ResourceModel\Ean as EanResource;
use Autogedal\EanTools\Model\ResultStorage;

class Validator
{
    public function __construct(
        private readonly EanColumnParser $parser,
        private readonly Normalizer $normalizer,
        private readonly EanResource $eanResource,
        private readonly ResultStorage $storage
    ) {
    }

    public function run(
        string $uploadedPath,
        ?string $uploadedName,
        string $format,
        ?string $runId = null
    ): string {
        $parsedRows = $this->parser->parseEanColumn($uploadedPath, $uploadedName);
        $normalized = $this->normalizer->normalizeList($parsedRows);

        $candidates = array_column($normalized['accepted'], 'value');
        $used = $this->eanResource->findUsedEans($candidates);

        [$free, $usedItems, $suspect] = $this->split($normalized['accepted'], $used);

        $rejected = array_merge($normalized['rejected'], $suspect);

        $runId ??= $this->storage->createRun('validator');

        $this->writeFree($runId, $free, $format);
        $this->writeUsed($runId, $usedItems, $used, $format);
        $this->writeProblems($runId, $rejected, $normalized['duplicatesInFile'], $format);

        $allFree = $free !== [] && count($free) === count($parsedRows);

        $this->storage->finish($runId, [
            (string)__('Rows read from file') => count($parsedRows),
            (string)__('Duplicates in file') => count($normalized['duplicatesInFile']),
            (string)__('Rejected codes') => count($rejected),
            (string)__('Codes already in use') => count($usedItems),
            (string)__('Available codes') => count($free),
        ], $allFree ? [
            (string)__('All codes in the file are valid and have not been used.'),
        ] : []);

        return $runId;
    }

    private function split(array $accepted, array $used): array
    {
        $free = [];
        $usedItems = [];
        $suspect = [];

        foreach ($accepted as $item) {
            if (isset($used[$item['value']])) {
                $usedItems[] = $item;
            } elseif ($item['status'] === Normalizer::STATUS_WARNING) {
                $suspect[] = $item;
            } else {
                $free[] = $item;
            }
        }

        return [$free, $usedItems, $suspect];
    }

    private function writeFree(string $runId, array $free, string $format): void
    {
        $fileName = $this->storage->buildFileName('available_eans', $format);
        $writer = $this->storage->openWriter($runId, $fileName, [
            (string)__('ean'),
        ], $format);

        foreach ($free as $item) {
            $writer->writeRow([
                $item['value'],
            ], [0]);
        }

        $writer->close();
        $this->storage->registerFile(
            $runId,
            $fileName,
            (string)__('Available (unused) EANs'),
            count($free),
            'success'
        );
    }

    private function writeUsed(string $runId, array $usedItems, array $used, string $format): void
    {
        $fileName = $this->storage->buildFileName('used_eans', $format);
        $writer = $this->storage->openWriter($runId, $fileName, [
            (string)__('ean'),
            (string)__('sku'),
            (string)__('sku_fz'),
            (string)__('name'),
            (string)__('manufacturer'),
        ], $format);

        $rows = 0;

        foreach ($usedItems as $item) {
            foreach ($used[$item['value']] as $product) {
                $writer->writeRow([
                    $item['value'],
                    $product['sku'],
                    $product['sku_fz'],
                    $product['name'],
                    $product['manufacturer'],
                ], [0]);
                $rows++;
            }
        }

        $writer->close();
        $this->storage->registerFile(
            $runId,
            $fileName,
            (string)__('Already used EANs'),
            $rows,
            'warning'
        );
    }

    private function writeProblems(string $runId, array $rejected, array $duplicates, string $format): void
    {
        $problems = array_merge($rejected, $duplicates);

        if ($problems === []) {
            return;
        }

        usort(
            $problems,
            static fn(array $a, array $b): int => [$a['issue'], $a['line']] <=> [$b['issue'], $b['line']]
        );

        $fileName = $this->storage->buildFileName('ean_issues', $format);
        $writer = $this->storage->openWriter($runId, $fileName, [
            (string)__('file_value'),
            (string)__('line'),
            (string)__('issue'),
            (string)__('details'),
        ], $format);

        foreach ($problems as $item) {
            $writer->writeRow([
                $item['original'],
                $item['line'],
                $this->normalizer->getIssueLabel((string)$item['issue']),
                $item['message'],
            ], [0]);
        }

        $writer->close();
        $this->storage->registerFile($runId, $fileName, (string)__('Codes with issues'), count($problems), 'error');
    }
}
