<?php
declare(strict_types=1);

namespace Autogedal\EanTools\ViewModel;

use Autogedal\EanTools\Model\ByteSize;
use Autogedal\EanTools\Model\ResultStorage;
use Autogedal\EanTools\Model\Tool\RunLabel;
use DateTime;
use DateTimeZone;
use IntlDateFormatter;
use Magento\Backend\Model\UrlInterface;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;
use Magento\Framework\View\Element\Block\ArgumentInterface;
use Psr\Log\LoggerInterface;
use Throwable;

class Runs implements ArgumentInterface
{
    private ?array $runs = null;
    private ?string $error = null;

    public function __construct(
        private readonly ResultStorage $storage,
        private readonly RunLabel $runLabel,
        private readonly ByteSize $byteSize,
        private readonly UrlInterface $urlBuilder,
        private readonly TimezoneInterface $timezone,
        private readonly LoggerInterface $logger
    ) {
    }

    public function getUrl(string $path, array $params = []): string
    {
        return $this->urlBuilder->getUrl($path, $params);
    }

    public function getRuns(): array
    {
        if ($this->runs === null) {
            $this->runs = [];
            try {
                foreach ($this->storage->listRuns() as $run) {
                    $this->runs[] = $this->present($run);
                }
            } catch (Throwable $exception) {
                $this->logger->error(
                    'Autogedal_EanTools runs: ' . $exception->getMessage(),
                    ['exception' => $exception]
                );
                $this->error = $exception->getMessage();
            }
        }

        return $this->runs;
    }

    public function getError(): ?string
    {
        $this->getRuns();

        return $this->error;
    }

    public function getTotals(): array
    {
        $runs = $this->getRuns();
        $files = 0;
        $size = 0;

        foreach ($runs as $run) {
            $files += count($run['files']);
            $size += (int)$run['size'];
        }

        return [
            [
                'label' => (string)__('Stored runs'),
                'value' => number_format((float)count($runs), 0, ',', '.'),
                'tone' => 'neutral',
            ],
            [
                'label' => (string)__('Available files'),
                'value' => number_format((float)$files, 0, ',', '.'),
                'tone' => 'neutral',
            ],
            [
                'label' => (string)__('Storage used'),
                'value' => $this->byteSize->format($size),
                'tone' => 'neutral',
            ],
        ];
    }

    public function getRetentionDays(): int
    {
        return $this->storage->getRetentionDays();
    }

    public function formatSize(int $bytes): string
    {
        return $this->byteSize->format($bytes);
    }

    private function present(array $run): array
    {
        $runId = (string)$run['run_id'];
        $status = (string)($run['status'] ?? ResultStorage::STATUS_COMPLETE);
        $files = [];
        $size = 0;

        foreach ((array)$run['files'] as $file) {
            $size += (int)$file['size'];
            $files[] = [
                'name' => (string)$file['name'],
                'label' => (string)$file['label'],
                'rows' => (int)$file['rows'],
                'tone' => (string)$file['tone'],
                'size' => (int)$file['size'],
                'empty' => (int)$file['rows'] === 0,
                'url' => $this->getUrl('autogedal_ean/download/index', [
                    'run' => $runId,
                    'file' => (string)$file['name'],
                ]),
            ];
        }

        return [
            'run_id' => $runId,
            'title' => $this->runLabel->getTitle((string)$run['tool']),
            'created_at' => $this->formatMoment((string)$run['created_at']) ?? $runId,
            'expires_at' => $this->formatDay((int)$run['expires_at']),
            'result_url' => $this->getUrl('autogedal_ean/result/index', ['run' => $runId]),
            'status' => $status,
            'status_label' => $this->getStatusLabel($status),
            'error' => isset($run['error']) ? (string)$run['error'] : null,
            'files' => $files,
            'size' => $size,
        ];
    }

    private function getStatusLabel(string $status): string
    {
        return match ($status) {
            ResultStorage::STATUS_QUEUED => (string)__('Queued'),
            ResultStorage::STATUS_PROCESSING => (string)__('Processing'),
            ResultStorage::STATUS_FAILED => (string)__('Failed'),
            default => (string)__('Completed'),
        };
    }

    private function formatMoment(string $utcDateTime): ?string
    {
        if ($utcDateTime === '') {
            return null;
        }

        try {
            return $this->timezone->formatDateTime(
                new DateTime($utcDateTime, new DateTimeZone('UTC')),
                IntlDateFormatter::MEDIUM,
                IntlDateFormatter::SHORT
            );
        } catch (Throwable) {
            return null;
        }
    }

    private function formatDay(int $timestamp): ?string
    {
        try {
            return $this->timezone->formatDateTime(
                new DateTime('@' . $timestamp),
                IntlDateFormatter::MEDIUM,
                IntlDateFormatter::NONE
            );
        } catch (Throwable) {
            return null;
        }
    }
}
