<?php

declare(strict_types=1);

namespace Kylin\Holiday\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

/**
 * 法定节假日服务
 * - 判断是否为法定节假日
 * - 判断是否为未成年人允许游戏日（周五/六/日+法定节假日）
 * - 获取下一个允许游戏时段
 */
class HolidayService
{
    /**
     * 判断指定日期是否为法定节假日
     */
    public function isHoliday(?\DateTimeImmutable $date = null): bool
    {
        $date = $date ?? new \DateTimeImmutable('today', new \DateTimeZone(config('kylin-holiday.timezone')));
        $dateStr = $date->format('Y-m-d');

        return Cache::remember("holiday:{$dateStr}", config('kylin-holiday.cache_ttl', 86400), function () use ($dateStr) {
            return DB::table('kh_holidays')
                ->where('date', $dateStr)
                ->where('is_holiday', 1)
                ->exists();
        });
    }

    /**
     * 判断是否为未成年人允许游戏日
     * 规则：周五、周六、周日 + 法定节假日
     */
    public function isAllowedDay(?\DateTimeImmutable $date = null): bool
    {
        $date = $date ?? new \DateTimeImmutable('today', new \DateTimeZone(config('kylin-holiday.timezone')));
        $dayOfWeek = (int) $date->format('N'); // 1=周一 7=周日

        // 周五(5)、周六(6)、周日(7)
        if (in_array($dayOfWeek, [5, 6, 7], true)) {
            return true;
        }

        // 法定节假日
        return $this->isHoliday($date);
    }

    /**
     * 获取下一个允许游戏时段窗口
     *
     * @return array{date: string, start: string, end: string}|null
     */
    public function getNextWindow(?\DateTimeImmutable $from = null): ?array
    {
        $from = $from ?? new \DateTimeImmutable('now', new \DateTimeZone(config('kylin-holiday.timezone')));
        $hourStart = (int) config('kylin-anti-addiction.minor_hour_start', 20);
        $hourEnd = (int) config('kylin-anti-addiction.minor_hour_end', 21);

        // 从今天开始查找，最多查30天
        for ($i = 0; $i < 30; $i++) {
            $checkDate = $from->modify("+{$i} days");
            if ($this->isAllowedDay($checkDate)) {
                return [
                    'date' => $checkDate->format('Y-m-d'),
                    'start' => sprintf('%02d:00', $hourStart),
                    'end' => sprintf('%02d:00', $hourEnd),
                ];
            }
        }

        return null;
    }

    /**
     * 判断当前是否在允许游戏时段内
     */
    public function isInAllowedWindow(?\DateTimeImmutable $now = null): bool
    {
        $now = $now ?? new \DateTimeImmutable('now', new \DateTimeZone(config('kylin-holiday.timezone')));

        if (!$this->isAllowedDay($now)) {
            return false;
        }

        $hourStart = (int) config('kylin-anti-addiction.minor_hour_start', 20);
        $hourEnd = (int) config('kylin-anti-addiction.minor_hour_end', 21);
        $currentHour = (int) $now->format('H');

        return $currentHour >= $hourStart && $currentHour < $hourEnd;
    }

    /**
     * 刷新节假日缓存
     */
    public function refreshCache(): void
    {
        $year = (int) date('Y');
        $holidays = DB::table('kh_holidays')
            ->whereYear('date', $year)
            ->where('is_holiday', 1)
            ->pluck('date')
            ->map(fn ($d) => (string) $d)
            ->toArray();

        Cache::put(config('kylin-holiday.cache_key', 'kylin:holidays'), $holidays, config('kylin-holiday.cache_ttl', 86400));

        $this->audit('holiday_cache_refreshed', [
            'year' => $year,
            'holiday_count' => count($holidays),
        ]);
    }

    /**
     * 记录节假日审计日志
     *
     * 依据《未成年人保护法》和防沉迷相关规定，
     * 节假日数据变更影响未成年人游戏时段判定，需记录审计日志。
     */
    private function audit(string $event, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => 'holiday',
                'model_id' => 0,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'holiday',
            ]);
        } catch (\Throwable $e) {
            Log::warning('节假日审计日志写入失败', [
                'error' => $e->getMessage(),
                'event' => $event,
            ]);
        }
    }
}
