<?php

namespace App\Representation\Service;

/**
 * Gregorian ↔ Jalali (Solar Hijri) conversion.
 */
class JalaliDateService
{
    public const TIMEZONE = 'Asia/Tehran';

    public function toJalali(\DateTimeInterface $date): array
    {
        [$gy, $gm, $gd] = [(int)$date->format('Y'), (int)$date->format('m'), (int)$date->format('d')];
        return $this->gregorianToJalali($gy, $gm, $gd);
    }

    /**
     * @return array{0: int, 1: int, 2: int} [سال، ماه، روز] شمسی
     *
     * پیاده‌سازی دستیِ قبلی غلط بود: `gregorianToJalali(2026, 7, 30)` مقدار
     * `[3006, 7, 3]` می‌داد به‌جای `[1405, 5, 8]` (مبدأ روزشمار ~۱۶۰۱ سال جابه‌جا
     * بود). چون `formatDateTime()` همین کلاس از قبل با `IntlDateFormatter` درست کار
     * می‌کرد، هر دو تبدیل هم به همان منتقل شدند تا یک منبع حقیقت بماند.
     */
    public function gregorianToJalali(int $gy, int $gm, int $gd): array
    {
        // ساعت ۱۲ ظهر عمدی است: نیمه‌شب در روزهای تغییر ساعت می‌تواند یک روز عقب/جلو بیفتد.
        $date = (new \DateTimeImmutable(
            sprintf('%04d-%02d-%02d 12:00:00', $gy, $gm, $gd),
            new \DateTimeZone(self::TIMEZONE),
        ))->getTimestamp();

        $formatted = $this->persianFormatter('yyyy-MM-dd')->format($date);
        [$jy, $jm, $jd] = array_map('intval', explode('-', $formatted));

        return [$jy, $jm, $jd];
    }

    /** تبدیل timestamp به رشته‌ی شمسی: «۱۴۰۵/۰۴/۰۲ ۰۹:۰۰» (پیش‌فرض با ساعت). */
    public function formatDateTime(int $timestamp, bool $withTime = true): string
    {
        $pattern = $withTime ? 'yyyy/MM/dd HH:mm' : 'yyyy/MM/dd';
        $f = new \IntlDateFormatter(
            'fa_IR@calendar=persian',
            \IntlDateFormatter::NONE,
            \IntlDateFormatter::NONE,
            self::TIMEZONE,
            \IntlDateFormatter::TRADITIONAL,
            $pattern,
        );
        return $f->format($timestamp);
    }

    public function jalaliYear(int $timestamp): int
    {
        return $this->gregorianToJalali(
            (int)date('Y', $timestamp),
            (int)date('m', $timestamp),
            (int)date('d', $timestamp)
        )[0];
    }

    public function jalaliMonth(int $timestamp): int
    {
        return $this->gregorianToJalali(
            (int)date('Y', $timestamp),
            (int)date('m', $timestamp),
            (int)date('d', $timestamp)
        )[1];
    }

    /** Returns [startTs, endTs] for a given Jalali month/year */
    public function jalaliMonthRange(int $jYear, int $jMonth): array
    {
        // Convert first day of Jalali month to Gregorian
        $start = $this->jalaliToGregorian($jYear, $jMonth, 1);
        $daysInMonth = $jMonth <= 6 ? 31 : ($jMonth <= 11 ? 30 : 29);
        $end   = $this->jalaliToGregorian($jYear, $jMonth, $daysInMonth);

        $startTs = mktime(0, 0, 0, $start[1], $start[2], $start[0]);
        $endTs   = mktime(23, 59, 59, $end[1], $end[2], $end[0]);

        return [$startTs, $endTs];
    }

    public function jalaliYearRange(int $jYear): array
    {
        $start = $this->jalaliToGregorian($jYear, 1, 1);
        $end   = $this->jalaliToGregorian($jYear, 12, 29);

        return [
            mktime(0, 0, 0, $start[1], $start[2], $start[0]),
            mktime(23, 59, 59, $end[1], $end[2], $end[0]),
        ];
    }

    /** @return array{0: int, 1: int, 2: int} [سال، ماه، روز] میلادی */
    public function jalaliToGregorian(int $jy, int $jm, int $jd): array
    {
        $calendar = \IntlCalendar::createInstance(
            new \DateTimeZone(self::TIMEZONE),
            'fa_IR@calendar=persian',
        );
        $calendar->clear();
        $calendar->set($jy, $jm - 1, $jd, 12, 0, 0);

        $date = (new \DateTimeImmutable('@' . intdiv((int) $calendar->getTime(), 1000)))
            ->setTimezone(new \DateTimeZone(self::TIMEZONE));

        return [(int) $date->format('Y'), (int) $date->format('n'), (int) $date->format('j')];
    }

    private function persianFormatter(string $pattern): \IntlDateFormatter
    {
        return new \IntlDateFormatter(
            'en_US@calendar=persian',
            \IntlDateFormatter::NONE,
            \IntlDateFormatter::NONE,
            self::TIMEZONE,
            \IntlDateFormatter::TRADITIONAL,
            $pattern,
        );
    }
}
