<?php

namespace Kylin\LawCase\Services;

use Kylin\LawCase\Models\Schedule;
use Kylin\LawCase\Events\ScheduleReminder;
use Illuminate\Support\Facades\Event;
use Carbon\Carbon;

/**
 * 日程服务
 *
 * 处理日程安排、提醒等业务逻辑。
 */
class ScheduleService
{
    public function createSchedule(array $data): Schedule
    {
        $data['user_id'] = auth()->id();

        // 排期冲突检测（F-CASE-004）
        if (!empty($data['start_time']) && !empty($data['end_time'])) {
            $conflicts = $this->detectConflicts(
                $data['user_id'],
                Carbon::parse($data['start_time']),
                Carbon::parse($data['end_time']),
                $data['case_id'] ?? null
            );

            if (!empty($conflicts)) {
                $conflictTitles = $conflicts->pluck('title')->implode('、');
                throw new \InvalidArgumentException("日程时间冲突，与以下日程重叠：{$conflictTitles}");
            }
        }

        $schedule = Schedule::create($data);

        // 如果设置了提醒，调度提醒事件
        if ($schedule->reminder_before && $schedule->reminder_type) {
            $this->scheduleReminder($schedule);
        }

        $this->audit('schedule_created', $schedule, ['title' => $schedule->title]);

        return $schedule;
    }

    public function updateSchedule(Schedule $schedule, array $data): Schedule
    {
        // 排期冲突检测（排除自身）
        if (!empty($data['start_time']) && !empty($data['end_time'])) {
            $userId = $data['user_id'] ?? $schedule->user_id;
            $conflicts = $this->detectConflicts(
                $userId,
                Carbon::parse($data['start_time']),
                Carbon::parse($data['end_time']),
                $data['case_id'] ?? $schedule->case_id,
                $schedule->id
            );

            if (!empty($conflicts)) {
                $conflictTitles = $conflicts->pluck('title')->implode('、');
                throw new \InvalidArgumentException("日程时间冲突，与以下日程重叠：{$conflictTitles}");
            }
        }

        $schedule->update($data);

        // 更新后重新调度提醒
        if ($schedule->reminder_before && $schedule->reminder_type) {
            $this->scheduleReminder($schedule->fresh());
        }

        $this->audit('schedule_updated', $schedule, ['title' => $schedule->title]);

        return $schedule->fresh();
    }

    public function deleteSchedule(Schedule $schedule): bool
    {
        $this->audit('schedule_deleted', $schedule, ['title' => $schedule->title]);

        return $schedule->delete();
    }

    /**
     * 排期冲突检测 - 检测指定律师在时间段内是否有日程重叠
     * F-CASE-004: 庭审日期安排与日历联动，支持排期冲突检测
     */
    public function detectConflicts(
        int $userId,
        Carbon $startTime,
        Carbon $endTime,
        ?int $caseId = null,
        ?int $excludeScheduleId = null
    ) {
        $query = Schedule::where('user_id', $userId)
            ->where(function ($q) use ($startTime, $endTime) {
                // 时间重叠条件：已有日程的开始时间 < 新日程结束时间 AND 已有日程的结束时间 > 新日程开始时间
                $q->where('start_time', '<', $endTime)
                  ->where('end_time', '>', $startTime);
            });

        if ($excludeScheduleId) {
            $query->where('id', '!=', $excludeScheduleId);
        }

        return $query->get();
    }

    public function getCalendarData(string $view, string $date): array
    {
        $carbonDate = Carbon::parse($date);
        $query = Schedule::with(['case', 'user']);

        return match ($view) {
            'month' => $this->getMonthData($query, $carbonDate),
            'week' => $this->getWeekData($query, $carbonDate),
            'day' => $this->getDayData($query, $carbonDate),
            default => $this->getMonthData($query, $carbonDate),
        };
    }

    /**
     * 调度日程提醒
     */
    private function scheduleReminder(Schedule $schedule): void
    {
        $reminderMinutes = match ($schedule->reminder_type) {
            'minutes' => $schedule->reminder_before,
            'hours' => $schedule->reminder_before * 60,
            'days' => $schedule->reminder_before * 1440,
            default => $schedule->reminder_before,
        };

        $reminderTime = $schedule->start_time->subMinutes($reminderMinutes);

        // 如果提醒时间已过，立即触发
        if ($reminderTime->isPast()) {
            Event::dispatch(new ScheduleReminder($schedule, 0));
        }
    }

    private function getMonthData($query, Carbon $date): array
    {
        $start = $date->copy()->startOfMonth();
        $end = $date->copy()->endOfMonth();

        return [
            'view' => 'month',
            'start' => $start->toDateString(),
            'end' => $end->toDateString(),
            'schedules' => $query->byDateRange($start, $end)->get(),
        ];
    }

    private function getWeekData($query, Carbon $date): array
    {
        $start = $date->copy()->startOfWeek();
        $end = $date->copy()->endOfWeek();

        return [
            'view' => 'week',
            'start' => $start->toDateString(),
            'end' => $end->toDateString(),
            'schedules' => $query->byDateRange($start, $end)->get(),
        ];
    }

    private function getDayData($query, Carbon $date): array
    {
        return [
            'view' => 'day',
            'date' => $date->toDateString(),
            'schedules' => $query->whereDate('start_time', $date)->get(),
        ];
    }

    /**
     * 记录日程审计日志
     *
     * 依据《律师法》和《个人信息保护法》，
     * 日程创建、修改、删除等操作需记录审计日志。
     */
    private function audit(string $event, Schedule $schedule, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => Schedule::class,
                'model_id' => $schedule->id,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'law-case-schedule',
            ]);
        } catch (\Throwable $e) {
            logger()->warning('日程审计日志写入失败', [
                'error' => $e->getMessage(),
                'schedule_id' => $schedule->id,
                'event' => $event,
            ]);
        }
    }
}
