<?php
declare(strict_types=1);

namespace Autogedal\EanTools\Model\Tool;

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

class Exporter
{
    public function __construct(
        private readonly EanResource $eanResource,
        private readonly ResultStorage $storage,
        private readonly StatusLabel $statusLabel,
        private readonly EanAudit $audit,
        private readonly Normalizer $normalizer
    ) {
    }

    public function exportMissing(array $filters, string $format, ?string $runId = null): string
    {
        $runId ??= $this->storage->createRun('missing');

        $fileName = $this->storage->buildFileName('products_without_ean', $format);
        $writer = $this->storage->openWriter($runId, $fileName, $this->productHeader(), $format);

        $count = $this->eanResource->streamProductsWithoutEan(
            $filters,
            function (array $row) use ($writer): void {
                $writer->writeRow($this->productColumns($row));
            }
        );

        $writer->close();
        $this->storage->registerFile($runId, $fileName, (string)__('Products without EANs'), $count, 'success');

        $this->storage->finish($runId, [
            (string)__('Status filter') => $this->describeStatus($filters['status'] ?? 'all'),
            (string)__('Product type filter') => ($filters['types'] ?? []) === []
                ? (string)__('All')
                : implode(', ', array_map('ucfirst', $filters['types'])),
            (string)__('Products found') => $count,
        ]);

        return $runId;
    }

    public function exportUsed(string $format, ?string $runId = null): string
    {
        $runId ??= $this->storage->createRun('used');

        $fileName = $this->storage->buildFileName('all_used_eans', $format);
        $writer = $this->storage->openWriter($runId, $fileName, array_merge(
            [(string)__('ean')],
            $this->productHeader()
        ), $format);

        $eans = 0;
        $currentEan = null;

        $rows = $this->eanResource->streamUsedEans(
            function (array $row) use ($writer, &$eans, &$currentEan): void {
                $ean = (string)$row['value'];
                if ($ean !== $currentEan) {
                    $currentEan = $ean;
                    $eans++;
                }

                $writer->writeRow(array_merge([$ean], $this->productColumns($row)), [0]);
            }
        );

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

        $this->storage->finish($runId, [
            (string)__('Exported EAN codes') => $eans,
            (string)__('Products') => $rows,
            (string)__('Attribute') => $this->eanResource->getEanAttributeCode(),
        ]);

        return $runId;
    }

    public function exportDuplicates(string $format, ?string $runId = null): string
    {
        $runId ??= $this->storage->createRun('duplicates');

        $columns = max(2, $this->eanResource->getMaxProductsPerDuplicateEan());

        $fileName = $this->storage->buildFileName('duplicate_eans', $format);
        $writer = $this->storage->openWriter($runId, $fileName, $this->duplicateHeader($columns), $format);

        $eans = 0;
        $currentEan = null;
        $group = [];

        $writeGroup = function () use ($writer, $columns, &$currentEan, &$group, &$eans): void {
            if ($currentEan === null) {
                return;
            }

            $writer->writeRow($this->duplicateRow($currentEan, $group, $columns), [0]);
            $eans++;
        };

        $products = $this->eanResource->streamDuplicateEans(
            function (array $row) use ($writeGroup, &$currentEan, &$group): void {
                $ean = (string)$row['value'];
                if ($ean !== $currentEan) {
                    $writeGroup();
                    $currentEan = $ean;
                    $group = [];
                }

                $group[] = $this->productColumns($row);
            }
        );

        $writeGroup();

        $writer->close();
        $this->storage->registerFile(
            $runId,
            $fileName,
            (string)__('Duplicate EANs and their products'),
            $eans,
            'warning'
        );

        $this->storage->finish($runId, [
            (string)__('Duplicate EAN codes') => $eans,
            (string)__('Affected products') => $products,
        ]);

        return $runId;
    }

    private function duplicateHeader(int $columns): array
    {
        $header = [(string)__('shared_ean')];

        foreach ($this->productHeader() as $field) {
            for ($i = 1; $i <= $columns; $i++) {
                $header[] = $field . '_' . $i;
            }
        }

        return $header;
    }

    private function duplicateRow(string $ean, array $products, int $columns): array
    {
        $row = [$ean];
        $fields = count($this->productHeader());

        for ($field = 0; $field < $fields; $field++) {
            for ($i = 0; $i < $columns; $i++) {
                $row[] = $products[$i][$field] ?? '';
            }
        }

        return $row;
    }

    public function exportInvalid(string $format, ?string $runId = null): string
    {
        $runId ??= $this->storage->createRun('invalid');

        $fileName = $this->storage->buildFileName('invalid_eans', $format);
        $writer = $this->storage->openWriter($runId, $fileName, array_merge(
            [
                (string)__('ean'),
                (string)__('issue'),
            ],
            $this->productHeader()
        ), $format);

        $labels = $this->normalizer->getStoredIssueLabels();
        $perIssue = array_fill_keys(array_keys($labels), 0);

        $rows = $this->audit->streamInvalid(
            function (array $row, array $check) use ($writer, &$perIssue): void {
                $perIssue[$check['issue']] = ($perIssue[$check['issue']] ?? 0) + 1;

                $writer->writeRow(array_merge(
                    [
                        $row['value'],
                        $check['message'],
                    ],
                    $this->productColumns($row)
                ), [0]);
            }
        );

        $writer->close();
        $this->storage->registerFile($runId, $fileName, (string)__('Products with invalid EANs'), $rows, 'warning');

        $summary = [(string)__('Products with invalid EANs') => $rows];
        foreach ($labels as $issue => $label) {
            $summary[$label] = $perIssue[$issue] ?? 0;
        }

        $this->storage->finish($runId, $summary);

        return $runId;
    }

    private function productHeader(): array
    {
        return [
            (string)__('sku'),
            (string)__('sku_fz'),
            (string)__('name'),
            (string)__('status'),
            (string)__('type'),
            (string)__('manufacturer'),
        ];
    }

    private function productColumns(array $row): array
    {
        return [
            (string)$row['sku'],
            (string)($row['sku_fz'] ?? ''),
            (string)($row['name'] ?? ''),
            $this->statusLabel->get($row['status'] ?? null),
            (string)$row['type_id'],
            (string)($row['manufacturer'] ?? ''),
        ];
    }

    private function describeStatus(string $status): string
    {
        return match ($status) {
            'enabled' => (string)__('Enabled products only'),
            'disabled' => (string)__('Disabled products only'),
            default => (string)__('All'),
        };
    }
}
