<?php
declare(strict_types=1);

namespace Autogedal\BlackFriday\Model;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\Stdlib\DateTime\TimezoneInterface;

class Config
{
    public const XML_ENABLED = 'autogedal_blackfriday/general/enabled';
    public const XML_START = 'autogedal_blackfriday/general/start_at';
    public const XML_END = 'autogedal_blackfriday/general/end_at';
    public const XML_PAGE_SIZE = 'autogedal_blackfriday/general/page_size';
    public const XML_TEST_MODE = 'autogedal_blackfriday/general/test_mode';
    public const XML_TEASER_CAPSULE = 'autogedal_blackfriday/teaser/capsule';
    public const XML_TEASER_STICKER = 'autogedal_blackfriday/teaser/sticker';
    public const XML_EXCLUDED = 'autogedal_blackfriday/products/excluded_skus';
    public const XML_CATEGORY_ORDER = 'autogedal_blackfriday/products/category_order';
    public const XML_BESTSELLER_DAYS = 'autogedal_blackfriday/products/bestseller_days';
    public const XML_EMAIL_TEMPLATE = 'autogedal_blackfriday/email/template';
    public const XML_EMAIL_IDENTITY = 'autogedal_blackfriday/email/identity';
    public const XML_EMAIL_BATCH = 'autogedal_blackfriday/email/batch_size';

    public function __construct(
        private readonly ScopeConfigInterface $scopeConfig,
        private readonly TimezoneInterface $timezone
    ) {
    }

    public function isEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(self::XML_ENABLED);
    }

    /** Unix timestamp of the campaign start (the value is entered in the store timezone). */
    public function getStartTimestamp(): int
    {
        return $this->toTimestamp((string)$this->scopeConfig->getValue(self::XML_START));
    }

    public function getEndTimestamp(): int
    {
        return $this->toTimestamp((string)$this->scopeConfig->getValue(self::XML_END));
    }

    public function isLive(?int $now = null): bool
    {
        return ($now ?? time()) >= $this->getStartTimestamp();
    }

    public function isOver(?int $now = null): bool
    {
        return ($now ?? time()) > $this->getEndTimestamp();
    }

    /** ISO 8601 with offset, for the countdown in the browser. */
    public function getStartIso(): string
    {
        return $this->toIso($this->getStartTimestamp());
    }

    public function getEndIso(): string
    {
        return $this->toIso($this->getEndTimestamp());
    }

    /** "6 noiembrie": the day the prices appear, used in the page texts. */
    public function getStartDayLabel(): string
    {
        $months = ['ianuarie', 'februarie', 'martie', 'aprilie', 'mai', 'iunie', 'iulie', 'august',
            'septembrie', 'octombrie', 'noiembrie', 'decembrie'];
        $date = (new \DateTimeImmutable('@' . $this->getStartTimestamp()))->setTimezone($this->zone());
        return $date->format('j') . ' ' . $months[(int)$date->format('n') - 1];
    }

    /** "6–25 Noiembrie" (or "28 Noiembrie – 2 Decembrie" across months), from the start and end dates. */
    public function getPeriodLabel(): string
    {
        $months = ['Ianuarie', 'Februarie', 'Martie', 'Aprilie', 'Mai', 'Iunie', 'Iulie', 'August',
            'Septembrie', 'Octombrie', 'Noiembrie', 'Decembrie'];
        $start = (new \DateTimeImmutable('@' . $this->getStartTimestamp()))->setTimezone($this->zone());
        $end = (new \DateTimeImmutable('@' . $this->getEndTimestamp()))->setTimezone($this->zone());
        if ($start->format('n') === $end->format('n')) {
            return $start->format('j') . '–' . $end->format('j') . ' ' . $months[(int)$end->format('n') - 1];
        }
        return $start->format('j') . ' ' . $months[(int)$start->format('n') - 1] . ' – ' . $end->format('j') . ' ' . $months[(int)$end->format('n') - 1];
    }

    /** Test panel on the page: switch between "before the campaign" and "live" (set manually from the admin). */
    public function isTestMode(): bool
    {
        return $this->scopeConfig->isSetFlag(self::XML_TEST_MODE);
    }

    /** Countdown capsule at the bottom left of the site pages. */
    public function isCapsuleEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(self::XML_TEASER_CAPSULE);
    }

    /** "BF" sticker by the logo of the site header. */
    public function isStickerEnabled(): bool
    {
        return $this->scopeConfig->isSetFlag(self::XML_TEASER_STICKER);
    }

    public function getPageSize(): int
    {
        return max(1, (int)$this->scopeConfig->getValue(self::XML_PAGE_SIZE) ?: 24);
    }

    /** @return string[] upper-cased SKUs excluded from the campaign */
    public function getExcludedSkus(): array
    {
        $raw = (string)$this->scopeConfig->getValue(self::XML_EXCLUDED);
        $skus = array_filter(array_map('trim', preg_split('/[,\n\r;]+/', $raw) ?: []), 'strlen');
        return array_values(array_unique(array_map('mb_strtoupper', $skus)));
    }

    /** @return int[] main category ids in display order */
    public function getCategoryOrder(): array
    {
        $raw = (string)$this->scopeConfig->getValue(self::XML_CATEGORY_ORDER);
        return array_values(array_unique(array_filter(array_map('intval', explode(',', $raw)))));
    }

    public function getBestsellerDays(): int
    {
        return max(1, (int)$this->scopeConfig->getValue(self::XML_BESTSELLER_DAYS) ?: 183);
    }

    public function getEmailTemplate(): string
    {
        return (string)$this->scopeConfig->getValue(self::XML_EMAIL_TEMPLATE);
    }

    public function getEmailIdentity(): string
    {
        return (string)$this->scopeConfig->getValue(self::XML_EMAIL_IDENTITY) ?: 'general';
    }

    public function getEmailBatchSize(): int
    {
        return max(1, (int)$this->scopeConfig->getValue(self::XML_EMAIL_BATCH) ?: 100);
    }

    private function toTimestamp(string $value): int
    {
        $date = \DateTimeImmutable::createFromFormat('Y-m-d H:i', trim($value), $this->zone());
        return $date ? $date->getTimestamp() : PHP_INT_MAX;
    }

    private function toIso(int $timestamp): string
    {
        return (new \DateTimeImmutable('@' . $timestamp))->setTimezone($this->zone())->format(DATE_ATOM);
    }

    private function zone(): \DateTimeZone
    {
        return new \DateTimeZone($this->timezone->getConfigTimezone() ?: 'Europe/Bucharest');
    }
}
