<?php
/**
 * VR票务插件 - 座位 SKU 服务
 *
 * 核心业务：批量生成座位级 SKU（spec_base + spec_value）
 * 旁路 GoodsSpecificationsInsert()，直接 SQL INSERT
 *
 * @package vr_ticket\service
 */

namespace app\plugins\vr_ticket\service;

class SeatSkuService extends BaseService
{
    /** @var int 分批处理每批条数 */
    const BATCH_SIZE = 500;

    /**
     * 批量生成座位级 SKU
     *
     * 遍历座位模板的 seat_map，为每个座位生成：
     * 1. goods_spec_base 行（inventory=1，价格从 zone.price 获取）
     * 2. goods_spec_value 行（4维度 × N座位 = 4N行）
     *
     * 幂等：已存在的座位（spec_value 中已有关联）不重复生成
     *
     * @param int $goodsId      商品ID
     * @param int $seatTemplateId 座位模板ID
     * @return array ['code' => 0, 'msg' => '...', 'data' => ['total' => N, 'generated' => N, 'spec_base_id_map' => ['seatId' => ['spec_base_id' => int, 'zone_id' => string, 'row' => string, 'col' => int], ...]]]
     *
     * spec_base_id_map 格式：前端 ticket_detail.html 使用 seatKey（如 "A_1"）作为 key，
     * 期望 value 为嵌套对象 {spec_base_id, zone_id, row, col}，而非 flat spec_base_id
     */
    public static function BatchGenerate(int $goodsId, int $seatTemplateId): array
    {
        $goodsId = intval($goodsId);
        $seatTemplateId = intval($seatTemplateId);

        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);
        if (empty($seatMap['map']) || empty($seatMap['seats'])) {
            return ['code' => -3, 'msg' => '座位模板 seat_map 数据无效'];
        }

        // 3. 获取/确认 VR 规格类型ID（$vr-场馆 / $vr-分区 / $vr-时段 / $vr-座位号）
        // v3.0: venue.name 优先，否则降级取模板 name 或默认值
        $venueName = $seatMap['venue']['name'] ?? $template['name'] ?? '未命名场馆';
        $specTypeIds = self::ensureVrSpecTypes($goodsId, $venueName);
        if ($specTypeIds['code'] !== 0) {
            return $specTypeIds;
        }
        $typeVenue  = $specTypeIds['data']['$vr-场馆'];
        $typeZone   = $specTypeIds['data']['$vr-分区'];
        $typeTime   = $specTypeIds['data']['$vr-时段'];
        $typeSeat   = $specTypeIds['data']['$vr-座位号'];

        // 4. 构建 section → price 映射（从 seat_map.sections 读）
        //    格式：section['name'] => section['price']（默认 0）
        $sectionPrices = [];
        foreach (($seatMap['sections'] ?? []) as $section) {
            $sectionPrices[$section['name'] ?? ''] = floatval($section['price'] ?? 0);
        }

        // 5. 收集所有座位数据
        $seats = [];  // [seatId => ['row' => int, 'col' => int, 'char' => string, 'label' => string, 'price' => float, 'zone' => string]]
        $map = $seatMap['map'];
        $rowLabels = $seatMap['row_labels'] ?? [];
        $seatsData = $seatMap['seats'] ?? [];

        foreach ($map as $rowIndex => $rowStr) {
            $rowLabel = $rowLabels[$rowIndex] ?? chr(65 + $rowIndex);
            $chars = mb_str_split($rowStr);
            foreach ($chars as $colIndex => $char) {
                if ($char === '_' || $char === '-' || !isset($seatsData[$char])) {
                    continue; // 跳过空座/通道/无效
                }
                $seatInfo = $seatsData[$char];
                $zoneName = $seatInfo['zone'] ?? ($seatInfo['section'] ?? '默认区');

                // 价格：优先用 seat_info.zone.price，没有则用 sectionPrices，最后用 seat_info.price
                $seatPrice = floatval($seatInfo['price'] ?? 0);
                if ($seatPrice == 0 && isset($sectionPrices[$zoneName])) {
                    $seatPrice = $sectionPrices[$zoneName];
                }

                $seatId = $rowLabel . '_' . ($colIndex + 1); // 唯一座位标识，与前端 specBaseIdMap key 格式一致（如 "A_1"）
                $seats[$seatId] = [
                    'row'       => $rowIndex,
                    'col'       => $colIndex,
                    'char'      => $char,
                    'label'     => $seatInfo['label'] ?? ($rowLabel . '排' . ($colIndex + 1) . '座'), // 如 "A排1座"
                    'price'     => $seatPrice,
                    'zone'      => $zoneName,
                    'row_label' => $rowLabel,
                    'col_num'   => $colIndex + 1,
                    'seat_key'  => $seatId,
                ];
            }
        }

        if (empty($seats)) {
            return ['code' => -4, 'msg' => '座位模板中未找到有效座位'];
        }

        // 6. 找出已存在的 spec_base_id（幂等：只处理新座位）
        $existingMap = self::getExistingSpecBaseIds($goodsId, $typeSeat);
        $newSeats = [];
        foreach ($seats as $seatId => $seat) {
            if (!isset($existingMap[$seatId])) {
                $newSeats[$seatId] = $seat;
            }
        }

        if (empty($newSeats)) {
            return [
                'code' => 0,
                'msg'  => '所有座位 SKU 已存在，无需重复生成',
                'data' => [
                    'total'            => count($seats),
                    'generated'        => 0,
                    'batch'            => 0,
                    'spec_base_id_map' => $existingMap,
                ],
            ];
        }

        // 7. 分批插入 goods_spec_base + goods_spec_value
        $now = time();
        $newSeatIds = array_keys($newSeats);
        $totalBatches = ceil(count($newSeatIds) / self::BATCH_SIZE);
        $generatedCount = 0;
        $specBaseIdMap = $existingMap; // 合并已存在和新生成的

        for ($batch = 0; $batch < $totalBatches; $batch++) {
            $batchSeatIds = array_slice($newSeatIds, $batch * self::BATCH_SIZE, self::BATCH_SIZE);
            $baseInsertData = [];
            $valueInsertData = [];

            foreach ($batchSeatIds as $seatId) {
                $seat = $newSeats[$seatId];

                // 1行 goods_spec_base
                $baseInsertData[] = [
                    'goods_id'       => $goodsId,
                    'price'          => $seat['price'],
                    'original_price' => $seat['price'],
                    'inventory'      => 1,
                    'buy_min_number' => 1,
                    'buy_max_number' => 1,
                    'weight'         => 0.00,
                    'volume'         => 0.00,
                    'coding'         => '',
                    'barcode'        => '',
                    'inventory_unit' => '座',
                    'extends'        => json_encode([
                        'seat_id'   => $seatId,
                        'seat_char' => $seat['char'],
                        'row_label' => $seat['row_label'],
                        'zone'      => $seat['zone'],
                        'label'     => $seat['label'],
                    ], JSON_UNESCAPED_UNICODE),
                    'add_time'       => $now,
                ];
            }

            // 批量插入 spec_base，获取自增ID
            $specBaseIds = self::batchInsertSpecBase($baseInsertData);

            // 构建并批量插入 spec_value（每个 base_id × 4维度）
            foreach ($specBaseIds as $idx => $specBaseId) {
                $seatId = $batchSeatIds[$idx];
                $seat = $newSeats[$seatId];

                // $vr-场馆
                $valueInsertData[] = [
                    'goods_id'           => $goodsId,
                    'goods_spec_base_id' => $specBaseId,
                    'spec_type_id'       => $typeVenue,
                    'value'              => $venueName,
                    'md5_key'            => md5($venueName),
                    'add_time'           => $now,
                ];
                // $vr-分区（zone 名称）
                $valueInsertData[] = [
                    'goods_id'           => $goodsId,
                    'goods_spec_base_id' => $specBaseId,
                    'spec_type_id'       => $typeZone,
                    'value'              => $seat['zone'],
                    'md5_key'            => md5($seat['zone']),
                    'add_time'           => $now,
                ];
                // $vr-时段（placeholder，后续由 UpdateSessionSku 替换）
                $valueInsertData[] = [
                    'goods_id'           => $goodsId,
                    'goods_spec_base_id' => $specBaseId,
                    'spec_type_id'       => $typeTime,
                    'value'              => '待选场次',
                    'md5_key'            => md5('待选场次'),
                    'add_time'           => $now,
                ];
                // $vr-座位号
                $valueInsertData[] = [
                    'goods_id'           => $goodsId,
                    'goods_spec_base_id' => $specBaseId,
                    'spec_type_id'       => $typeSeat,
                    'value'              => $seat['label'],
                    'md5_key'            => md5($seat['label']),
                    'add_time'           => $now,
                ];

                $specBaseIdMap[$seatId] = [
                    'spec_base_id' => $specBaseId,
                    'zone_id'      => $seat['zone'],
                    'row'          => $seat['row_label'],
                    'col'          => $seat['col_num'],
                ];
                $generatedCount++;
            }

            // 批量插入 spec_value
            if (!empty($valueInsertData)) {
                self::batchInsertSpecValue($valueInsertData);
            }
        }

        // 8. 更新座位模板的 spec_base_id_map 字段
        self::updateTemplateSpecMap($seatTemplateId, $specBaseIdMap);

        self::log('BatchGenerate: done', [
            'goods_id'   => $goodsId,
            'template_id'=> $seatTemplateId,
            'total'      => count($seats),
            'generated'  => $generatedCount,
            'batches'    => $totalBatches,
        ]);

        return [
            'code' => 0,
            'msg'  => "生成完成，共 {$generatedCount} 个座位 SKU（分 {$totalBatches} 批）",
            'data' => [
                'total'            => count($seats),
                'generated'        => $generatedCount,
                'batch'            => $totalBatches,
                'spec_base_id_map' => $specBaseIdMap,
            ],
        ];
    }

    /**
     * 确保 VR 规格类型存在
     *
     * @param int $goodsId
     * @return array
     */
    private static function ensureVrSpecTypes(int $goodsId, string $venueName = '未命名场馆'): array
    {
        $now = time();
        $specTypeNames = ['$vr-场馆', '$vr-分区', '$vr-时段', '$vr-座位号'];
        $defaultValues = [
            '$vr-场馆'   => json_encode([['name' => $venueName, 'images' => '']]),
            '$vr-分区'   => '[{"name":"A区","images":""},{"name":"B区","images":""},{"name":"C区","images":""}]',
            '$vr-时段'   => '[{"name":"待选场次","images":""}]',
            '$vr-座位号' => '[{"name":"待选座位","images":""}]',
        ];

        $typeIds = [];
        foreach ($specTypeNames as $name) {
            $existing = \Db::name('GoodsSpecType')
                ->where('goods_id', $goodsId)
                ->where('name', $name)
                ->find();

            if (!empty($existing)) {
                $typeIds[$name] = intval($existing['id']);
            } else {
                $id = \Db::name('GoodsSpecType')->insertGetId([
                    'goods_id' => $goodsId,
                    'name'     => $name,
                    'value'    => $defaultValues[$name],
                    'add_time' => $now,
                ]);
                $typeIds[$name] = $id;
            }
        }

        // 确保商品启用多规格
        \Db::name('Goods')->where('id', $goodsId)->update([
            'is_exist_many_spec' => 1,
            'upd_time' => $now,
        ]);

        return ['code' => 0, 'data' => $typeIds];
    }

    /**
     * 批量插入 goods_spec_base，返回自增ID列表
     *
     * @param array $data 二维数组
     * @return array 自增ID列表
     */
    private static function batchInsertSpecBase(array $data): array
    {
        if (empty($data)) {
            return [];
        }

        $table = \Db::name('GoodsSpecBase')->getTable();
        $columns = array_keys($data[0]);
        $placeholders = implode(',', array_fill(0, count($data), '(' . implode(',', array_fill(0, count($columns), '?')) . ')'));
        $values = [];
        foreach ($data as $row) {
            foreach ($columns as $col) {
                $values[] = $row[$col];
            }
        }

        $sql = "INSERT INTO {$table} (" . implode(',', $columns) . ") VALUES {$placeholders}";
        \Db::execute($sql, $values);

        // 获取本批插入的自增ID
        $lastId = (int) \Db::query("SELECT LAST_INSERT_ID()")[0]['LAST_INSERT_ID()'] ?? 0;
        $count = count($data);
        $ids = [];
        for ($i = 0; $i < $count; $i++) {
            $ids[] = $lastId + $i;
        }
        return $ids;
    }

    /**
     * 批量插入 goods_spec_value
     *
     * @param array $data 二维数组
     */
    private static function batchInsertSpecValue(array $data): void
    {
        if (empty($data)) {
            return;
        }

        $table = \Db::name('GoodsSpecValue')->getTable();
        $columns = array_keys($data[0]);
        $placeholders = implode(',', array_fill(0, count($data), '(' . implode(',', array_fill(0, count($columns), '?')) . ')'));
        $values = [];
        foreach ($data as $row) {
            foreach ($columns as $col) {
                $values[] = $row[$col];
            }
        }

        $sql = "INSERT INTO {$table} (" . implode(',', $columns) . ") VALUES {$placeholders}";
        \Db::execute($sql, $values);
    }

    /**
     * 获取已存在的座位 spec_base_id 映射（幂等用）
     *
     * @param int $goodsId
     * @param int $typeSeatId $vr-座位号 spec_type_id
     * @return array [seatId => ['spec_base_id' => int, 'zone_id' => string, 'row' => string, 'col' => int]]
     *
     * 注意：zone_id / row / col 字段在从旧数据（flat格式）读取时为空字符串，
     * 前端 ticket_detail.html 只需要 spec_base_id 即可
     */
    private static function getExistingSpecBaseIds(int $goodsId, int $typeSeatId): array
    {
        // 从 goods_spec_value 中找 $vr-座位号 的记录
        // value 字段存储的是 seat_label（如 "A排1座"），从中解析出 seatId（如 "A_1"）
        $rows = \Db::name('GoodsSpecValue')
            ->where('goods_id', $goodsId)
            ->where('spec_type_id', $typeSeatId)
            ->column('goods_spec_base_id', 'value');

        if (empty($rows)) {
            return [];
        }

        $seatIdMap = [];
        foreach ($rows as $seatLabel => $baseId) {
            // 从 seat_label 解析 seatId（如 "A排1座" → "A_1"）
            // 格式: "{rowLabel}排{colNum}座"
            if (preg_match('/^([A-Za-z]+)排(\d+)座$/', $seatLabel, $m)) {
                $rowLabel = $m[1];
                $colNum = intval($m[2]);
                $seatId = $rowLabel . '_' . $colNum;
                $seatIdMap[$seatId] = [
                    'spec_base_id' => intval($baseId),
                    'zone_id'      => '',
                    'row'          => $rowLabel,
                    'col'          => $colNum,
                ];
            }
        }

        return $seatIdMap;
    }

    /**
     * 更新座位模板的 spec_base_id_map 字段
     *
     * @param int $templateId
     * @param array $specBaseIdMap
     */
    private static function updateTemplateSpecMap(int $templateId, array $specBaseIdMap): void
    {
        \Db::name(self::table('seat_templates'))
            ->where('id', $templateId)
            ->update([
                'spec_base_id_map' => json_encode($specBaseIdMap, JSON_UNESCAPED_UNICODE),
                'upd_time'         => time(),
            ]);
    }

    /**
     * 按场次更新座位 SKU 的 $vr-时段 维度
     *
     * 当用户选择具体场次后，将所有座位的"待选场次"替换为实际场次时间
     *
     * @param int    $goodsId     商品ID
     * @param int    $seatTemplateId 座位模板ID
     * @param string $sessionName 场次名称（如 "2026-05-01 19:00"）
     * @param float  $sessionPrice 场次价格（可选，用于替换价格）
     * @return array
     */
    public static function UpdateSessionSku(int $goodsId, int $seatTemplateId, string $sessionName, float $sessionPrice = 0.0): array
    {
        $goodsId = intval($goodsId);
        $seatTemplateId = intval($seatTemplateId);

        // 获取 $vr-时段 type_id
        $timeType = \Db::name('GoodsSpecType')
            ->where('goods_id', $goodsId)
            ->where('name', '$vr-时段')
            ->find();
        if (empty($timeType)) {
            return ['code' => -1, 'msg' => '$vr-时段 规格类型不存在，请先调用 BatchGenerate()'];
        }
        $typeTimeId = intval($timeType['id']);

        // 找出所有"待选场次"的 spec_value 行
        $待选Rows = \Db::name('GoodsSpecValue')
            ->where('goods_id', $goodsId)
            ->where('spec_type_id', $typeTimeId)
            ->where('value', '待选场次')
            ->select()
            ->toArray();

        if (empty($待选Rows)) {
            return ['code' => 0, 'msg' => '没有需要更新的场次', 'data' => ['updated' => 0]];
        }

        $now = time();
        $updatedCount = 0;
        foreach ($待选Rows as $row) {
            \Db::name('GoodsSpecValue')
                ->where('id', $row['id'])
                ->update([
                    'value'    => $sessionName,
                    'md5_key'  => md5($sessionName),
                    'add_time' => $now,
                ]);
            $updatedCount++;
        }

        // 如果提供了场次价格，更新对应 spec_base 的价格
        if ($sessionPrice > 0) {
            $待选BaseIds = array_column($待选Rows, 'goods_spec_base_id');
            \Db::name('GoodsSpecBase')
                ->whereIn('id', $待选BaseIds)
                ->update([
                    'price' => $sessionPrice,
                    'original_price' => $sessionPrice,
                ]);
        }

        self::log('UpdateSessionSku: done', [
            'goods_id' => $goodsId,
            'session'  => $sessionName,
            'updated'  => $updatedCount,
        ]);

        return [
            'code' => 0,
            'msg'  => "更新 {$updatedCount} 个座位的场次信息",
            'data' => ['updated' => $updatedCount],
        ];
    }
}
