<?php

/**
 * 文件功能：节日福利调度计算服务
 *
 * 统一负责节日福利模板的首次触发时间与下一次触发时间推导，
 * 支持普通重复模式与年度节日高级调度。
 */

namespace App\Services;

use App\Models\HolidayEvent;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;

/**
 * 类功能：计算节日福利模板的下一次触发时间。
 */
class HolidayEventScheduleService
{
    /**
     * 根据表单配置计算模板的下一次触发时间。
     *
     * @param  array<string, mixed>  $data
     */
    public function resolveNextConfiguredSendAt(array $data, ?CarbonInterface $reference = null): CarbonImmutable
    {
        $referenceTime = CarbonImmutable::instance($reference ?? now());

        if (($data['repeat_type'] ?? 'once') !== 'yearly') {
            return CarbonImmutable::parse((string) $data['send_at']);
        }

        return $this->findNextYearlyOccurrence($data, $referenceTime);
    }

    /**
     * 计算模板在一次自动触发后的下一次 send_at。
     */
    public function advanceAfterTrigger(HolidayEvent $event): ?CarbonImmutable
    {
        if ($event->send_at === null) {
            return null;
        }

        $currentSendAt = CarbonImmutable::instance($event->send_at);

        return match ($event->repeat_type) {
            'daily' => $currentSendAt->addDay(),
            'weekly' => $currentSendAt->addWeek(),
            'monthly' => $currentSendAt->addMonth(),
            'yearly' => $this->findNextYearlyOccurrence($this->extractYearlyConfig($event), $currentSendAt->addSecond()),
            default => null,
        };
    }

    /**
     * 查找年度节日配置在参考时间之后的下一次触发点。
     *
     * @param  array<string, mixed>  $data
     */
    public function findNextYearlyOccurrence(array $data, CarbonInterface $reference): CarbonImmutable
    {
        $referenceTime = CarbonImmutable::instance($reference);

        foreach ([$referenceTime->year, $referenceTime->year + 1, $referenceTime->year + 2] as $year) {
            foreach ($this->buildYearlyOccurrencesForYear($data, $year) as $occurrence) {
                if ($occurrence->greaterThanOrEqualTo($referenceTime)) {
                    return $occurrence;
                }
            }
        }

        return $this->buildYearlyOccurrencesForYear($data, $referenceTime->year + 3)[0];
    }

    /**
     * 构造指定年份内的全部年度节日触发点。
     *
     * @param  array<string, mixed>  $data
     * @return array<int, CarbonImmutable>
     */
    public function buildYearlyOccurrencesForYear(array $data, int $year): array
    {
        [$hour, $minute] = array_map('intval', explode(':', (string) $data['schedule_time']));

        $baseDate = CarbonImmutable::create(
            $year,
            (int) $data['schedule_month'],
            (int) $data['schedule_day'],
            $hour,
            $minute,
            0,
            config('app.timezone')
        );

        $occurrences = [];
        $durationDays = max(1, (int) ($data['duration_days'] ?? 1));
        $dailyOccurrences = max(1, (int) ($data['daily_occurrences'] ?? 1));
        $intervalMinutes = (int) ($data['occurrence_interval_minutes'] ?? 0);

        for ($dayIndex = 0; $dayIndex < $durationDays; $dayIndex++) {
            $dayStart = $baseDate->addDays($dayIndex);

            for ($occurrenceIndex = 0; $occurrenceIndex < $dailyOccurrences; $occurrenceIndex++) {
                $occurrences[] = $dayStart->addMinutes($intervalMinutes * $occurrenceIndex);
            }
        }

        return $occurrences;
    }

    /**
     * 从模板模型中提取年度节日调度字段。
     *
     * @return array<string, mixed>
     */
    private function extractYearlyConfig(HolidayEvent $event): array
    {
        return [
            'schedule_month' => $event->schedule_month,
            'schedule_day' => $event->schedule_day,
            'schedule_time' => $event->schedule_time,
            'duration_days' => $event->duration_days,
            'daily_occurrences' => $event->daily_occurrences,
            'occurrence_interval_minutes' => $event->occurrence_interval_minutes,
        ];
    }
}
