<?php

namespace App\Services\Admin\YeWu;

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

use Exception;

class RosterService
{
    // 资源缓存，避免重复查询
    private $resourceCache = [];

    public function generatePlans($dateRange, $planModelIds, $userId, $dateType = 1, $holidayEnable = 1)
    {
        // 1. 基础参数校验
        if (!is_array($dateRange) || count($dateRange) != 2 || empty($planModelIds)) {
            throw new Exception('日期范围或模板ID参数错误');
        }

        // 2. 获取模板信息并校验状态
        $models = DB::table('s_source_roster')
            ->whereIn('id', $planModelIds)
            ->get();

        if ($models->isEmpty()) {
            throw new Exception('未找到有效的排班模板');
        }

        foreach ($models as $model) {
            if ($model->status != 1 || $model->is_del != 0) {
                throw new Exception("模板状态异常，请重新选择！异常模板Id: {$model->id}");
            }
        }

        // 获取部门ID (用于查重，取第一个模板的部门ID，假设批量操作通常针对同一部门)
        // 如果业务允许跨部门混合勾选，这里可能需要调整查重逻辑（按部门分组查）
        $department_id = $models->first()->department_id;

        $start_date = new DateTime($dateRange[0]);
        $end_date = new DateTime($dateRange[1]);

        // ==========================================
        // 【新增】1. 整体校验：模板与资源时令配置匹配
        // ==========================================
        $this->validateTemplates($models);

        // ==========================================
        // 【新增】2. 整体校验：资源时段冲突检测
        // ==========================================
        $this->checkResourceTimeConflict($models, $start_date, $end_date);

        // ==========================================
        // 【核心】3. 重复性检测 (在事务外执行，提高性能)
        // ==========================================
        $this->checkDuplicateRecords(
            $department_id,
            $planModelIds,
            $start_date,
            $end_date,
            $models
        );
        // 如果上面没抛异常，说明没有重复，继续往下执行

        // 4. 获取节假日列表
        $holiday_list = DB::table('s_holiday')
            ->whereBetween('date', $dateRange)
            ->where(['type' => 2])
            ->pluck('date')
            ->toArray();

        $success_count = 0;

        // 5. 开启事务
        DB::beginTransaction();
        try {
            $current_date = clone $start_date;

            while ($current_date <= $end_date) {
                $current_date_str = $current_date->format('Y-m-d');

                // --- 逻辑判断：节假日过滤 ---

                // 如果是"仅节假日"模式 (date_type == 2)，且当天不是节假日 -> 跳过
                if ($dateType == 2 && !in_array($current_date_str, $holiday_list)) {
                    $current_date->modify('+1 day');
                    continue;
                }

                // 如果"节假日不可用" (HolidayEnable == 0)，且当天是节假日 -> 跳过
                if ($holidayEnable == 0 && in_array($current_date_str, $holiday_list)) {
                    $current_date->modify('+1 day');
                    continue;
                }

                // --- 获取星期 ---
                $weekday = (int)$current_date->format('w');
                $weekname = $this->getWeekName($weekday);

                foreach ($models as $model) {
                    // --- 逻辑判断：星期匹配 ---
                    // 如果是"按星期"模式 (date_type == 1) 且模板也是按星期定义的，必须星期一致
                    if ($dateType == 1 && isset($model->date_type) && $model->date_type == 1) {
                        if ($model->weekname !== $weekname) {
                            continue;
                        }
                    }

                    // 【新增】时令校验：跳过不在时令范围内的日期
                    $resource = $this->getCachedResource($model->resources_id);
                    if (!$this->isDateInSeasonalRange($current_date, $model->type, $resource)) {
                        continue; // 跳过该模板在此日期的生成
                    }

                    // --- 构造插入数据 ---
                    $data = [
                        'roster_id' => $model->id,
                        'date' => $current_date_str,
                        'weekname' => $weekname,
                        'department_id' => $model->department_id,
                        'resources_id' => $model->resources_id ?? null,
                        'device_id' => $model->device_id ?? null,
                        'period_id' => $model->period_id ?? null,
                        'patient_type' => $model->patient_type ?? null,
                        'begin_time' => $model->begin_time,
                        'end_time' => $model->end_time,
                        'end_reservation_time' => $model->end_reservation_time ?? null,
                        'time_unit' => $model->time_unit ?? null,
                        'status' => 1,
                        'adduser' => $userId,
                        'is_del' => 0,
                        'created_at' => date('Y-m-d H:i:s'),
                        'updated_at' => date('Y-m-d H:i:s'),
                    ];

                    // --- 插入主表 ---
                    $plan_id = DB::table('s_source_roster_detail')->insertGetId($data);

                    if (!$plan_id) {
                        throw new Exception("号源明细插入失败，日期：{$current_date_str}, 模板ID：{$model->id}");
                    }
                    $success_count++;

                    // --- 插入关联表：数量配置 ---
                    $this->insertCountInfo($plan_id, $model->id);

                    // --- 插入关联表：设备配置 ---
                    // 注意：原逻辑如果 device_id 为空会报错，这里保持原逻辑
                    $this->insertDeviceInfo($plan_id, $model->device_id, $model->id);
                }

                $current_date->modify('+1 day');
            }

            // 6. 提交事务
            DB::commit();

            return ['success' => true, 'count' => $success_count];

        } catch (Exception $e) {
            // 7. 异常回滚
            DB::rollBack();
            // 记录日志
            Log::error('Roster Generation Failed: ' . $e->getMessage(), [
                'dateRange' => $dateRange,
                'user_id' => $userId
            ]);
            // 重新抛出，让 Controller 处理
            throw $e;
        }
    }

    /**
     * 整体校验：模板与资源时令配置必须严格匹配
     * - 模板 type=1/2 → 资源 time_mode=1
     * - 模板 type=0 → 资源 time_mode=0
     */
    private function validateTemplates($models)
    {
        foreach ($models as $model) {
            // 获取资源信息
            $resource = $this->getCachedResource($model->resources_id);

            if (!$resource) {
                throw new Exception("模板关联的资源不存在，模板ID: {$model->id}");
            }

            // 严格一对一匹配
            if ($model->type != 0 && $resource->time_mode != 1) {
                $errorMsg = "模板ID {$model->id} 配置了时令(type={$model->type})，但资源未开启时令，请检查资源配置！";
                Log::error($errorMsg);
                throw new Exception($errorMsg);
            }

            if ($model->type == 0 && $resource->time_mode != 0) {
                $errorMsg = "模板ID {$model->id} 未配置时令(type=0)，但资源开启了时令，请检查资源配置！";
                Log::error($errorMsg);
                throw new Exception($errorMsg);
            }
        }
    }

    /**
     * 资源级别查重：同一资源同一天的时间段不能重叠
     * 同一资源同一天可以有多个不同时段，但是时段不能交差
     * 边界判断：不重叠（一个时间点结束，另一个可以开始）
     */
    private function checkResourceTimeConflict($models, $startDate, $endDate)
    {
        $startStr = $startDate->format('Y-m-d');
        $endStr = $endDate->format('Y-m-d');

        foreach ($models as $model) {
            // 查询该资源在日期范围内已存在的排班
            $existingPlans = DB::table('s_source_roster_detail')
                ->where('resources_id', $model->resources_id)
                ->where('date', '>=', $startStr)
                ->where('date', '<=', $endStr)
                ->where('is_del', 0)
                ->get();

            foreach ($existingPlans as $existing) {
                // 检查时间是否重叠（同一天才检查）
                $existingDate = $existing->date;
                // 使用模板的星期作为参考日期
                $templateDate = $this->getTemplateDate($model);
                if ($existingDate === $templateDate) {
                    if ($this->isTimeOverlap(
                        $model->begin_time,
                        $model->end_time,
                        $existing->begin_time,
                        $existing->end_time
                    )) {
                        throw new Exception(
                            "资源ID [{$model->resources_id}] 在 {$existingDate} 存在时段冲突！" .
                            "新增: {$model->begin_time}-{$model->end_time} " .
                            "与已存在的 {$existing->begin_time}-{$existing->end_time} 重叠"
                        );
                    }
                }
            }
        }
    }

    /**
     * 判断两个时段是否重叠
     * 不重叠返回 false
     * 边界判断：不重叠（一个时间点结束，另一个可以开始）
     * 时段1: [start1, end1) 时段2: [start2, end2)
     * 重叠条件：时段1的结束时间 > 时段2的开始时间 && 时段2的结束时间 > 时段1的开始时间
     */
    private function isTimeOverlap($start1, $end1, $start2, $end2)
    {
        return ($end1 > $start2) && ($end2 > $start1);
    }

    /**
     * 时令日期匹配：判断日期是否在时令范围内
     * - 如果资源 time_mode != 1 或模板 type == 0，不需要时令校验，返回 true
     * - 否则检查日期是否在对应季节的周期范围内
     */
    private function isDateInSeasonalRange($currentDate, $modelType, $resource)
    {
        // 不需要时令校验
        if ($resource->time_mode != 1 || $modelType == 0) {
            return true;
        }

        $currentMMDD = $currentDate->format('m-d');

        // 解析 time_range（从缓存中获取）
        $time_range = json_decode($resource->time_range, true);

        if (!$time_range || !is_array($time_range)) {
            return false;
        }

        // 遍历所有季节周期
        foreach ($time_range as $season) {
            // 转换类型进行匹配（字符串 "1"/"2" vs 整数 1/2）
            if ((string)($season['type'] ?? '') === (string)$modelType) {
                // 检查是否在任意周期范围内
                if (isset($season['periods']) && is_array($season['periods'])) {
                    foreach ($season['periods'] as $period) {
                        $start = $period['start'] ?? '';
                        $end = $period['end'] ?? '';

                        if (!empty($start) && !empty($end) &&
                            $currentMMDD >= $start && $currentMMDD <= $end) {
                            return true; // 匹配成功
                        }
                    }
                }
            }
        }

        return false; // 不在时令范围内
    }

    /**
     * 获取缓存的资源信息，避免重复查询
     */
    private function getCachedResource($resourceId)
    {
        if (!isset($this->resourceCache[$resourceId])) {
            $this->resourceCache[$resourceId] = DB::table('s_department_resources')
                ->where('id', $resourceId)
                ->first();
        }
        return $this->resourceCache[$resourceId];
    }

    /**
     * 获取模板的日期（用于资源查重时的日期比较）
     * 注意：这里仅用于比较，实际生成时按循环中的日期
     */
    private function getTemplateDate($model)
    {
        // 如果模板有固定日期，返回固定日期
        if (isset($model->date)) {
            return $model->date;
        }
        // 否则返回 null（表示需要按循环日期比较）
        return null;
    }

    /**
     * 独立的重复检测方法
     * 如果发现重复，直接抛出包含详细信息的异常
     */
    private function checkDuplicateRecords($department_id, $planModelIds, $startDate, $endDate, $models)
    {
        $startStr = $startDate->format('Y-m-d');
        $endStr = $endDate->format('Y-m-d');

        // 查询已存在的记录
        $checkList = DB::table('s_source_roster_detail')
            ->where('department_id', $department_id)
            ->whereIn('roster_id', $planModelIds)
            ->where('date', '>=', $startStr)
            ->where('date', '<=', $endStr)
            ->where('is_del', 0)
            ->get();

        if ($checkList->isNotEmpty()) {
            // 构造详细的错误提示信息 (完全还原你原代码的逻辑)
            $msg = '已有重复的计划明细，禁止创建！当前选中的';
            $msglist = '';
            $msgIds = '';

            // 优化：将检查结果转为映射数组，避免双重循环 O(N*M)
            // key: roster_id, value: array of items
            $checkMap = [];
            foreach ($checkList as $item) {
                if (!isset($checkMap[$item->roster_id])) {
                    $checkMap[$item->roster_id] = [];
                }
                $checkMap[$item->roster_id][] = $item;
            }

            foreach ($models as $model) {
                if (isset($checkMap[$model->id])) {
                    foreach ($checkMap[$model->id] as $item) {
                        $msglist .= $item->date . ' ';
                        $msgIds .= $item->id . ' ';
                        // 拼接模板信息
                        $msg .= "" . $model->weekname . $model->begin_time . '-' . $model->end_time . " ";
                    }
                }
            }

            $fullErrorMessage = $msg . '已存在相同记录,</br>存在于:</br>' . $msglist . '</br>对应记录Id为：' . $msgIds . '</br>请先删除后再操作';

            // 抛出异常，中断流程
            throw new Exception($fullErrorMessage);
        }
    }

    private function getWeekName($weekday) {
        $map = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        return $map[$weekday] ?? '';
    }

    private function insertCountInfo($detailId, $rosterId) {
        $model_count_info = DB::table('s_source_roster_count')->where(['roster_id' => $rosterId])->get();

        if ($model_count_info->isEmpty()) {
            throw new Exception("模板数量信息异常，请重新选择！异常模板Id: {$rosterId}");
        }

        foreach ($model_count_info as $info) {
            $success = DB::table('s_source_roster_detail_count')->insert([
                'roster_detail_id' => $detailId,
                'appointment_type_id' => $info->appointment_type_id,
                'count' => $info->count,
                'max_total' => $info->max_total,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ]);
            if (!$success) {
                throw new Exception("渠道数量创建失败");
            }
        }
    }

    private function insertDeviceInfo($detailId, $deviceIdStr, $modelId) {
        // 保持原逻辑：如果模板没配设备，视为异常
        if (empty($deviceIdStr)) {
            throw new Exception("模板未关联设备，请重新选择！异常模板Id: {$modelId}");
        }

        $device_ids = explode(",", $deviceIdStr);
        foreach ($device_ids as $dv_value) {
            $dv_value = trim($dv_value);
            if ($dv_value === '') continue;

            $success = DB::table('s_source_roster_detail_device')->insert([
                'roster_detail_id' => $detailId,
                'device_id' => $dv_value,
                'created_at' => date('Y-m-d H:i:s'),
                'updated_at' => date('Y-m-d H:i:s'),
            ]);

            if (!$success) {
                throw new Exception("设备关联创建失败");
            }
        }
    }
}
