<?php
/**
 * VR票务插件 - 座位 SKU 服务
 *
 * ShopXO 规格表结构：
 *   GoodsSpecType  : id, goods_id, name, value(JSON数组), add_time
 *   GoodsSpecBase  : id, goods_id, price, original_price, inventory, ...
 *   GoodsSpecValue : id, goods_id, goods_spec_base_id, value, md5_key, add_time
 *
 * 列对应关系由 GoodsEditSpecifications 决定：
 *   它把每个 GoodsSpecValue.value 逐个在 GoodsSpecType.value(JSON) 的 name 字段中搜索，
 *   匹配成功才显示在对应列。
 *   因此：GoodsSpecType.value JSON 里必须包含我们写入的所有 value 字符串。
 *
 * @package vr_ticket\service
 */

namespace app\plugins\vr_ticket\service;

use think\facade\Db;

class SeatSkuService extends BaseService
{
    const BATCH_SIZE = 200;

    /**
     * VR 规格维度名（顺序固定）
     */
    const SPEC_DIMS = ['$vr-场馆', '$vr-分区', '$vr-座位号', '$vr-场次'];

    /**
     * 批量生成座位级 SKU
     *
     * @param int    $goodsId
     * @param int    $seatTemplateId
     * @param array  $selectedRooms     要生成的厅 id 列表，空=全部
     * @param array  $selectedSections  按 roomId 为 key 的分区 char 数组，空=全部
     * @param array  $sessions          场次数组 e.g. [["start"=>"08:00","end"=>"23:59"]]
     */
    public static function BatchGenerate(
        int   $goodsId,
        int   $seatTemplateId,
        array $selectedRooms    = [],
        array $selectedSections = [],
        array $sessions         = []
    ): array {
        if ($goodsId <= 0 || $seatTemplateId <= 0) {
            return ['code' => -1, 'msg' => '参数错误：goodsId 或 seatTemplateId 无效'];
        }

        // 1. 加载座位模板
        $template = Db::name(self::table('seat_templates'))
            ->where('id', $seatTemplateId)
            ->find();
        if (empty($template)) {
            return ['code' => -2, 'msg' => "座位模板 {$seatTemplateId} 不存在"];
        }

        // 2. 解析 seat_map
        $seatMap = json_decode($template['seat_map'] ?? '{}', true);
        $rooms   = $seatMap['rooms'] ?? [];
        if (empty($rooms)) {
            return ['code' => -3, 'msg' => '座位模板 seat_map 无效（rooms 为空）'];
        }

        // 使用模板表的短名称
        $venueName = $template['name'] ?? '未命名场馆';

        // 3. 场次处理（默认兜底）
        if (empty($sessions)) {
            $sessions = [['start' => '08:00', 'end' => '23:59']];
        }
        $sessionStrings = [];
        foreach ($sessions as $s) {
            $start = is_array($s) ? ($s['start'] ?? '08:00') : '08:00';
            $end   = is_array($s) ? ($s['end']   ?? '23:59') : '23:59';
            $sessionStr = "{$start}-{$end}";
            if (!in_array($sessionStr, $sessionStrings)) {
                $sessionStrings[] = $sessionStr;
            }
        }

        // 4. 确保 4 个 VR spec type 维度存在，同时收集要写入的所有唯一 value
        //    我们先遍历座位图，收集全部规格值，再一次性写入 type.value JSON
        $selectedRooms    = array_values(array_filter($selectedRooms));
        $selectedSections = array_filter($selectedSections);

        // 按维度收集唯一值（用 有序列表 + 去重）
        $dimUniqueValues = [
            '$vr-场馆'  => [],
            '$vr-分区'  => [],
            '$vr-座位号' => [],
            '$vr-场次'  => [],
        ];

        // 5. 遍历地图，收集所有座位信息
        $seatsToInsert = [];
        foreach ($rooms as $rIdx => $room) {
            // 与前端 PHP 预处理保持一致：id 缺失时用 'room_{index}'
            $roomId   = !empty($room['id']) ? $room['id'] : ('room_' . $rIdx);
            $roomName = $room['name'] ?? '默认放映室';

            if (!empty($selectedRooms) && !in_array($roomId, $selectedRooms)) {
                continue;
            }

            // char → price 映射
            $sectionPrices = [];
            foreach (($room['sections'] ?? []) as $sec) {
                if (!empty($sec['char'])) {
                    $sectionPrices[$sec['char']] = floatval($sec['price'] ?? 0);
                }
            }

            $map       = $room['map']   ?? [];
            $seatsData = $room['seats'] ?? [];

            foreach ($map as $rowIndex => $rowStr) {
                $rowLabel = chr(65 + $rowIndex);
                $chars    = mb_str_split($rowStr);

                foreach ($chars as $colIndex => $char) {
                    if ($char === '_' || $char === '-' || !isset($seatsData[$char])) {
                        continue;
                    }

                    if (!empty($selectedSections[$roomId])
                        && !in_array($char, $selectedSections[$roomId])) {
                        continue;
                    }

                    // 价格三级 fallback
                    $seatInfo  = $seatsData[$char];
                    $seatPrice = floatval($seatInfo['price'] ?? 0);
                    if ($seatPrice == 0 && isset($sectionPrices[$char])) {
                        $seatPrice = $sectionPrices[$char];
                    }
                    if ($seatPrice == 0) {
                        foreach (($room['sections'] ?? []) as $sec) {
                            if (($sec['char'] ?? '') === $char) {
                                $seatPrice = floatval($sec['price'] ?? 0);
                                break;
                            }
                        }
                    }

                    $col = $colIndex + 1;

                    // 维度值字符串（确保非空）
                    $val_venue   = $venueName;
                    $val_section = $venueName . '-' . $roomName . '-' . $char;
                    $val_seat    = $venueName . '-' . $roomName . '-' . $char . '-' . $rowLabel . $col;

                    foreach ($sessionStrings as $sessionStr) {
                        $seatId = $roomId . '_' . $rowLabel . '_' . $col . '_' . md5($sessionStr);

                        $seatsToInsert[$seatId] = [
                            'price'       => $seatPrice,
                            'spec_values' => [
                                $val_venue,
                                $val_section,
                                $val_seat,
                                $sessionStr,
                            ],
                        ];

                        // 收集唯一维度值（保持首次出现顺序）
                        if (!in_array($val_venue, $dimUniqueValues['$vr-场馆'])) {
                            $dimUniqueValues['$vr-场馆'][] = $val_venue;
                        }
                        if (!in_array($val_section, $dimUniqueValues['$vr-分区'])) {
                            $dimUniqueValues['$vr-分区'][] = $val_section;
                        }
                        if (!in_array($val_seat, $dimUniqueValues['$vr-座位号'])) {
                            $dimUniqueValues['$vr-座位号'][] = $val_seat;
                        }
                        if (!in_array($sessionStr, $dimUniqueValues['$vr-场次'])) {
                            $dimUniqueValues['$vr-场次'][] = $sessionStr;
                        }
                    }
                }
            }
        }

        if (empty($seatsToInsert)) {
            return ['code' => -4, 'msg' => '无有效座位可生成'];
        }

        // 6. 保证 4 个 VR spec type 存在，并把已收集的 value 写入 type.value JSON
        //    这样 GoodsEditSpecifications 的 name 匹配才能命中每条 GoodsSpecValue
        self::ensureAndFillVrSpecTypes($goodsId, $dimUniqueValues);

        // 7. 写入 GoodsSpecBase + GoodsSpecValue
        $now            = time();
        $generatedCount = 0;
        $valueBatch     = [];

        foreach ($seatsToInsert as $seatId => $s) {
            $baseId = Db::name('GoodsSpecBase')->insertGetId([
                'goods_id'       => $goodsId,
                'price'          => $s['price'],
                'original_price' => 0,
                'inventory'      => 1,
                'buy_min_number' => 1,
                'buy_max_number' => 1,
                'weight'         => 0,
                'volume'         => 0,
                'coding'         => '',
                'barcode'        => '',
                'add_time'       => $now,
            ]);

            if (!$baseId) {
                throw new \Exception("GoodsSpecBase 写入失败 (seat: {$seatId})");
            }

            // 4 条 GoodsSpecValue，每条对应一个维度
            foreach ($s['spec_values'] as $specVal) {
                $valueBatch[] = [
                    'goods_id'           => $goodsId,
                    'goods_spec_base_id' => $baseId,
                    'value'              => (string)$specVal,
                    'md5_key'            => md5((string)$specVal),
                    'add_time'           => $now,
                ];
            }

            $generatedCount++;

            if (count($valueBatch) >= self::BATCH_SIZE) {
                Db::name('GoodsSpecValue')->insertAll($valueBatch);
                $valueBatch = [];
            }
        }

        if (!empty($valueBatch)) {
            Db::name('GoodsSpecValue')->insertAll($valueBatch);
        }

        return [
            'code' => 0,
            'msg'  => '生成成功',
            'data' => [
                'total'     => count($seatsToInsert),
                'generated' => $generatedCount,
            ],
        ];
    }

    /**
     * 幂等确保 4 个 VR 维度存在，并把本次收集的所有唯一值合并写入 type.value JSON
     *
     * 关键：GoodsEditSpecifications 通过 type.value JSON 里的 name 做匹配，
     * 所以每条 GoodsSpecValue.value 都必须在对应 type.value 的某个 name 里找到。
     *
     * @param int   $goodsId
     * @param array $dimUniqueValues  ['$vr-场馆' => [...], '$vr-分区' => [...], ...]
     */
    public static function ensureAndFillVrSpecTypes(int $goodsId, array $dimUniqueValues = []): void
    {
        $now = time();

        // 读取已存在的 VR 维度（按顺序）
        $existing = Db::name('GoodsSpecType')
            ->where('goods_id', $goodsId)
            ->whereIn('name', self::SPEC_DIMS)
            ->order('id', 'asc')
            ->select()
            ->toArray();

        $existingByName = array_column($existing, null, 'name');

        foreach (self::SPEC_DIMS as $dimName) {
            // 构建该维度的 value JSON（把所有唯一值合并，含已有值）
            $newItems = [];
            $existingItems = [];
            if (isset($existingByName[$dimName])) {
                $existingItems = json_decode($existingByName[$dimName]['value'] ?? '[]', true);
                if (!is_array($existingItems)) $existingItems = [];
            }

            // 把现有 JSON 中的 name 提取出来
            $existingNames = array_column($existingItems, 'name');

            // 合并本次新增的值
            $toAdd = $dimUniqueValues[$dimName] ?? [];
            foreach ($toAdd as $val) {
                if (!in_array($val, $existingNames)) {
                    $existingItems[] = ['name' => (string)$val, 'images' => ''];
                    $existingNames[] = $val;
                }
            }

            $valueJson = json_encode($existingItems, JSON_UNESCAPED_UNICODE);

            if (isset($existingByName[$dimName])) {
                // 更新已有维度的 value JSON
                Db::name('GoodsSpecType')
                    ->where('id', $existingByName[$dimName]['id'])
                    ->update(['value' => $valueJson]);
            } else {
                // 插入缺失维度（保持 SPEC_DIMS 顺序，ID 递增）
                Db::name('GoodsSpecType')->insert([
                    'goods_id' => $goodsId,
                    'name'     => $dimName,
                    'value'    => $valueJson,
                    'add_time' => $now,
                ]);
            }
        }
    }

    /**
     * 重新计算商品基础信息（价格区间、总库存）
     */
    public static function refreshGoodsBase(int $goodsId): array
    {
        $bases = Db::name('GoodsSpecBase')
            ->where('goods_id', $goodsId)
            ->select()
            ->toArray();

        if (empty($bases)) {
            return ['code' => -1, 'msg' => '无 GoodsSpecBase'];
        }

        $prices    = array_column($bases, 'price');
        $minPrice  = min($prices);
        $maxPrice  = max($prices);
        $inventory = array_sum(array_column($bases, 'inventory'));

        $priceDisplay = ($minPrice != $maxPrice && $maxPrice > 0)
            ? $minPrice . '-' . $maxPrice : $minPrice;

        Db::name('Goods')->where('id', $goodsId)->update([
            'min_price'          => $minPrice,
            'max_price'          => $maxPrice,
            'price'              => $priceDisplay,
            'min_original_price' => 0,
            'max_original_price' => 0,
            'original_price'     => 0,
            'inventory'          => $inventory,
            'is_exist_many_spec' => 1,
            'buy_min_number'     => 1,
            'buy_max_number'     => 1,
            'upd_time'           => time(),
        ]);

        return ['code' => 0];
    }

    /**
     * 获取商品前端展示数据（供 ticket_detail.html 模板使用）
     *
     * @param int $goodsId
     * @return array ['vr_seat_template' => [...], 'goods_spec_data' => [...]]
     */
    public static function GetGoodsViewData(int $goodsId): array
    {
        // 读取 vr_goods_config
        $goods = \think\facade\Db::name('goods')->find($goodsId);
        $vrGoodsConfig = json_decode($goods['vr_goods_config'] ?? '', true);

        if (empty($vrGoodsConfig) || !is_array($vrGoodsConfig)) {
            return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
        }

        // 取第一个配置块（单模板模式）
        $config = $vrGoodsConfig[0];
        $templateId = intval($config['template_id'] ?? 0);
        if ($templateId <= 0) {
            return ['vr_seat_template' => null, 'goods_spec_data' => [], 'goods_config' => null];
        }

        // 读取座位模板（包含 seat_map 和 spec_base_id_map）
        $seatTemplate = \think\facade\Db::name(self::table('seat_templates'))
            ->where('id', $templateId)
            ->find();

        // 解码 seat_map JSON（存储时是 JSON 字符串）
        if (!empty($seatTemplate['seat_map'])) {
            $decoded = json_decode($seatTemplate['seat_map'], true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $seatTemplate['seat_map'] = $decoded;
            }
        }

        // 解码 spec_base_id_map JSON
        if (!empty($seatTemplate['spec_base_id_map'])) {
            $decoded = json_decode($seatTemplate['spec_base_id_map'], true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $seatTemplate['spec_base_id_map'] = $decoded;
            }
        }

        // 构建场次列表（goods_spec_data）
        $sessions = $config['sessions'] ?? [];
        $goodsSpecData = [];

        foreach ($sessions as $session) {
            $start = $session['start'] ?? '';
            $end = $session['end'] ?? '';
            $timeRange = $start && $end ? "{$start}-{$end}" : ($start ?: $end);

            // 查找该场次对应的 spec_base_id
            $specValue = \think\facade\Db::name('goods_spec_value')
                ->alias('sv')
                ->join('goods_spec_base sb', 'sb.id = sv.goods_spec_base_id')
                ->where('sv.goods_id', $goodsId)
                ->where('sv.value', $timeRange)
                ->where('sb.price', '>', 0)
                ->find();

            $goodsSpecData[] = [
                'spec_id'   => $specValue['goods_spec_base_id'] ?? 0,
                'spec_name' => $timeRange,
                'price'     => $specValue['price'] ?? floatval($goods['price'] ?? 0),
            ];
        }

        // 如果没有从配置读取到场次，尝试从数据库直接读取场次类规格值
        if (empty($goodsSpecData)) {
            $sessionValues = \think\facade\Db::name('goods_spec_value')
                ->alias('sv')
                ->join('goods_spec_base sb', 'sb.id = sv.goods_spec_base_id')
                ->field('sv.goods_spec_base_id as spec_id, sv.value as spec_name, sb.price')
                ->where('sv.goods_id', $goodsId)
                ->where('sb.price', '>', 0)
                ->order('sb.id asc')
                ->select()->toArray();

            foreach ($sessionValues as $sv) {
                if (preg_match('/^\d{2}:\d{2}-\d{2}:\d{2}$/', $sv['spec_name'])) {
                    $goodsSpecData[] = [
                        'spec_id'   => $sv['spec_id'],
                        'spec_name' => $sv['spec_name'],
                        'price'     => floatval($sv['price']),
                    ];
                }
            }
        }

        return [
            'vr_seat_template' => $seatTemplate ?: null,
            'goods_spec_data'  => $goodsSpecData,
            'goods_config'     => $config,
        ];
    }
}
