<?php
/**
 * VR票务插件 - 票务服务
 *
 * 核心业务：订单支付成功 → 生成电子票
 *
 * @package vr_ticket\service
 */

namespace app\plugins\vr_ticket\service;

require_once __DIR__ . '/BaseService.php';

class TicketService extends BaseService
{
    /**
     * 订单支付成功回调
     *
     * 从 plugin.json 的 hook 触发：
     * plugins_service_order_pay_success_handle_end
     *
     * @param array $params 钩子参数，含 business_data, user_id, business_ids(order_ids)
     * @return bool
     */
    public static function onOrderPaid($params = [])
    {
        $order_id = $params['order_id'] ?? $params['business_id'] ?? ($params['business_ids'][0] ?? 0);
        BaseService::log('onOrderPaid: called', ['order_id' => $order_id, 'params_keys' => array_keys($params)], 'info');
        if (empty($order_id)) {
            BaseService::log('onOrderPaid: empty order_id', $params, 'warning');
            return false;
        }

        // 查询订单
        $order = \think\facade\Db::name('order')->find($order_id);
        if (empty($order) || $order['pay_status'] != 1) {
            BaseService::log('onOrderPaid: order not paid or not found', ['order_id' => $order_id], 'warning');
            return false;
        }

        // 查询订单明细（规格信息存储在 spec JSON 字段）
        $order_goods = \think\facade\Db::name('order_detail')
            ->where('order_id', $order_id)
            ->select()->toArray();

        if (empty($order_goods)) {
            BaseService::log('onOrderPaid: no order detail', ['order_id' => $order_id], 'error');
            return false;
        }

        // 逐行解析 spec JSON，提取座位信息
        foreach ($order_goods as &$og) {
            // 判断是否为票务商品（goods_id 在 order_detail 而非 order 主表）
            if (!BaseService::isTicketGoods($og['goods_id'])) {
                BaseService::log('onOrderPaid: not a ticket goods', ['order_id' => $order_id, 'goods_id' => $og['goods_id']], 'info');
                continue;
            }

            $spec_list = json_decode($og['spec'] ?? '[]', true);
            $spec_name = '';
            $spec_base_id = 0;

            // 完整解析 5 维规格
            $parsed = [
                'session'  => '',
                'venue'    => '',
                'studio'   => '',
                'section'  => '',
                'seat'     => '',
            ];
            if (is_array($spec_list)) {
                foreach ($spec_list as $spec_item) {
                    $type  = $spec_item['type']  ?? '';
                    $value = $spec_item['value'] ?? '';
                    switch ($type) {
                        case '$vr-场次':   $parsed['session']  = $value; break;
                        case '$vr-场馆':   $parsed['venue']    = $value; break;
                        case '$vr-演播室': $parsed['studio']   = $value; break;
                        case '$vr-分区':   $parsed['section']  = $value; break;
                        case '$vr-座位号': $parsed['seat']     = $value; break;
                    }
                }
            }
            // seat_info 格式：场次|场馆|演播室|分区|座位号（WalletService::parseSeatInfo 依赖此格式）
            $seat_info = implode('|', [
                $parsed['session'],
                $parsed['venue'],
                $parsed['studio'],
                $parsed['section'],
                $parsed['seat'],
            ]);
            // goods_snapshot 完整快照（含全部 5 维）
            $goods_snapshot = json_encode([
                'goods_name'  => $og['title']  ?? '',
                'item_type'  => 'ticket',
                'session'    => $parsed['session'],
                'venue'      => $parsed['venue'],
                'studio'     => $parsed['studio'],
                'section'    => $parsed['section'],
                'seat'       => $parsed['seat'],
                'price'      => $og['price'] ?? 0,
            ], JSON_UNESCAPED_UNICODE);

            $og['_parsed_spec_name'] = $parsed['seat'] ?: $parsed['section'];
            $og['_parsed_seat_info'] = $seat_info;
            $og['_parsed_goods_snapshot'] = $goods_snapshot;
        }
        unset($og);

        // 逐个生成票（每个订单明细行 = 一张票）
        $count = 0;
        foreach ($order_goods as $og) {
            $ticket_id = self::issueTicket($order, $og);
            if ($ticket_id > 0) {
                $count++;
            }
        }

        BaseService::log('onOrderPaid: success', [
            'order_id' => $order_id,
            'tickets_issued' => $count,
        ]);

        // 清除座位图缓存（确保下一位买家看到最新库存）
        if ($count > 0) {
            SeatMapService::ClearCache(intval($goodsId));
        }

        return $count > 0;
    }

    /**
     * 发放单张票
     *
     * @param array $order  订单数据
     * @param array $og     订单商品数据（来自 vrt_order_detail，已解析 _parsed_spec_name 等字段）
     * @return int 票ID
     */
    public static function issueTicket($order, $og)
    {
        $spec_name   = $og['_parsed_spec_name'] ?? '';
        $spec_base_id = $og['_parsed_spec_base_id'] ?? 0;
        $seat_info    = $og['_parsed_seat_info'] ?? $spec_name;
        $goods_snapshot = $og['_parsed_goods_snapshot'] ?? json_encode([
            'goods_name' => $og['title'] ?? '',
            'spec_name'  => $spec_name,
            'price'      => $og['price'] ?? 0,
        ], JSON_UNESCAPED_UNICODE);

        // P0-1 幂等保护：同一订单+同一座位名只发一张票
        $existing = \think\facade\Db::name(BaseService::table('tickets'))
            ->where('order_id', $order['id'])
            ->where('seat_info', $spec_name)
            ->find();
        if (!empty($existing)) {
            BaseService::log('issueTicket: idempotent_skip', [
                'order_id'  => $order['id'],
                'seat_info' => $spec_name,
            ], 'info');
            return $existing['id'];
        }

        $ticket_code = BaseService::generateUuid();
        $now = BaseService::now();

        // Step 1: 先插入获取 ticket_id（用于 short_code 生成）
        $ticket_id = \think\facade\Db::name(BaseService::table('tickets'))->insertGetId([
            'order_id'       => $order['id'],
            'order_no'       => $order['order_no'],
            'goods_id'       => $og['goods_id'],
            'goods_snapshot' => $goods_snapshot,
            'user_id'        => $order['user_id'],
            'ticket_code'    => $ticket_code,
            'qr_data'        => '',  // 占位，生成后更新
            'seat_info'      => $seat_info,
            'spec_base_id'   => $spec_base_id,
            'real_name'      => '',
            'phone'          => '',
            'id_card'        => '',
            'verify_status'  => 0,
            'issued_at'      => $now,
            'created_at'     => $now,
            'updated_at'     => $now,
        ]);

        if ($ticket_id <= 0) {
            BaseService::log('issueTicket: insert_failed', ['order_id' => $order['id']], 'error');
            return 0;
        }

        // Step 2: 生成短码（goods_id 明文 + ticket_id 混淆）
        // 短码存储在 qr_data 中，供前端展示
        $short_code = BaseService::shortCodeEncode($og['goods_id'], $ticket_id);

        // Step 3: 生成 QR payload（HMAC-SHA256 签名，30分钟有效，含 ticket_code）
        $qr_payload = BaseService::signQrPayload([
            'id'   => $ticket_id,
            'g'    => $og['goods_id'],
            'code' => $ticket_code,
            'iat'  => $now,
            'exp'  => $now + 1800, // 30分钟
        ]);

        // qr_data 格式：短码|QR_payload（竖线分隔）
        $qr_data = $short_code . '|' . $qr_payload;

        // Step 4: 更新 qr_data
        \think\facade\Db::name(BaseService::table('tickets'))
            ->where('id', $ticket_id)
            ->update(['qr_data' => $qr_data]);

        // Step 5: 写入观演人信息
        $extension_data = json_decode($order['extension_data'] ?? '{}', true);
        $attendee = $extension_data['attendee'] ?? [];

        \think\facade\Db::name(BaseService::table('tickets'))
            ->where('id', $ticket_id)
            ->update([
                'real_name' => $attendee['real_name'] ?? '',
                'phone'    => $attendee['phone'] ?? '',
                'id_card'  => $attendee['id_card'] ?? '',
            ]);

        BaseService::log('issueTicket: success', [
            'ticket_id'   => $ticket_id,
            'short_code'  => $short_code,
            'goods_id'    => $og['goods_id'],
        ]);

        return $ticket_id;
    }

    /**
     * 核销票（事务保护 + 悲观锁防并发）
     *
     * @param string $ticket_code 票码
     * @param int    $verifier_id 核销员ID
     * @return array [code, msg]
     */
    public static function verifyTicket($ticket_code, $verifier_id)
    {
        try {
            return \think\facade\Db::transaction(function () use ($ticket_code, $verifier_id) {
                // FOR UPDATE 悲观锁：防止并发核销同一张票
                $ticket = \think\facade\Db::name(BaseService::table('tickets'))
                    ->where('ticket_code', $ticket_code)
                    ->lock(true)
                    ->find();

                if (empty($ticket)) {
                    return ['code' => -1, 'msg' => '票码不存在'];
                }

                if ($ticket['verify_status'] == 1) {
                    return ['code' => -2, 'msg' => '该票已核销'];
                }

                if ($ticket['verify_status'] == 2) {
                    return ['code' => -3, 'msg' => '该票已退款'];
                }

                $now = BaseService::now();

                // 更新票状态
                \think\facade\Db::name(BaseService::table('tickets'))
                    ->where('id', $ticket['id'])
                    ->update([
                        'verify_status' => 1,
                        'verify_time'   => $now,
                        'verifier_id'    => $verifier_id,
                        'updated_at'     => $now,
                    ]);

                // 写入核销记录
                $verifier = \think\facade\Db::name(BaseService::table('verifiers'))
                    ->where('id', $verifier_id)
                    ->find();

                \think\facade\Db::name(BaseService::table('verifications'))->insert([
                    'ticket_id'    => $ticket['id'],
                    'ticket_code'  => $ticket_code,
                    'verifier_id'  => $verifier_id,
                    'verifier_name'=> $verifier['name'] ?? '',
                    'goods_id'     => $ticket['goods_id'],
                    'created_at'   => $now,
                ]);

                BaseService::log('verifyTicket: success', [
                    'ticket_id'   => $ticket['id'],
                    'verifier_id' => $verifier_id,
                ]);

                // 审计日志
                AuditService::logVerify(
                    $ticket['id'],
                    $ticket_code,
                    $verifier_id,
                    $verifier['name'] ?? '',
                    'success',
                    0
                );

                return [
                    'code' => 0,
                    'msg'  => '核销成功',
                    'data' => [
                        'seat_info'  => $ticket['seat_info'],
                        'real_name'  => $ticket['real_name'],
                        'goods_name' => json_decode($ticket['goods_snapshot'] ?? '{}', true)['goods_name'] ?? '',
                    ],
                ];
            });
        } catch (\Throwable $e) {
            BaseService::log('verifyTicket: transaction_error', [
                'ticket_code' => $ticket_code,
                'error'       => $e->getMessage(),
            ], 'error');
            return ['code' => -999, 'msg' => '核销失败，请重试'];
        }
    }

    /**
     * 获取用户所有票
     */
    public static function getUserTickets($user_id, $status = null)
    {
        $where = ['user_id' => $user_id];
        if ($status !== null) {
            $where['verify_status'] = $status;
        }
        return \think\facade\Db::name(BaseService::table('tickets'))
            ->where($where)
            ->order('created_at', 'desc')
            ->select();
    }

    /**
     * 通过短码核销票（自动路由）
     *
     * 短码结构：【明文 goods_id(4位)】【混淆 ticket_id(5位)】
     * 解码 O(1)：直接读前4位=goods_id，Feistel解密后5位=ticket_id
     *
     * @param string $short_code 短码
     * @param int    $verifier_id 核销员ID
     * @return array [code, msg]
     */
    public static function verifyByShortCode($short_code, $verifier_id)
    {
        try {
            // Step 1: 解码短码
            $decoded = BaseService::shortCodeDecode($short_code);
            $goods_id = $decoded['goods_id'];
            $ticket_id = $decoded['ticket_id'];

            // Step 2: DB 查询
            $ticket = \think\facade\Db::name(BaseService::table('tickets'))
                ->where('id', $ticket_id)
                ->where('goods_id', $goods_id)
                ->find();

            if (empty($ticket)) {
                BaseService::log('verifyByShortCode: ticket_not_found', [
                    'short_code' => $short_code,
                    'goods_id'   => $goods_id,
                    'ticket_id'  => $ticket_id,
                ], 'warning');
                return ['code' => -1, 'msg' => '票不存在'];
            }

            // Step 3: 委托给 verifyTicket（统一核销逻辑 + 事务 + 悲观锁）
            return self::verifyTicketById($ticket['id'], $verifier_id);

        } catch (\Throwable $e) {
            BaseService::log('verifyByShortCode: error', [
                'short_code' => $short_code,
                'error'      => $e->getMessage(),
            ], 'error');
            return ['code' => -999, 'msg' => '核销失败，请重试'];
        }
    }

    /**
     * 通过 ticket_id 核销票（内部方法）
     *
     * @param int $ticket_id 票ID
     * @param int $verifier_id 核销员ID
     * @return array [code, msg]
     */
    private static function verifyTicketById($ticket_id, $verifier_id)
    {
        try {
            return \think\facade\Db::transaction(function () use ($ticket_id, $verifier_id) {
                // FOR UPDATE 悲观锁：防止并发核销同一张票
                $ticket = \think\facade\Db::name(BaseService::table('tickets'))
                    ->where('id', $ticket_id)
                    ->lock(true)
                    ->find();

                if (empty($ticket)) {
                    return ['code' => -1, 'msg' => '票不存在'];
                }

                if ($ticket['verify_status'] == 1) {
                    return ['code' => -2, 'msg' => '该票已核销'];
                }

                if ($ticket['verify_status'] == 2) {
                    return ['code' => -3, 'msg' => '该票已退款'];
                }

                $now = BaseService::now();

                // 更新票状态
                \think\facade\Db::name(BaseService::table('tickets'))
                    ->where('id', $ticket_id)
                    ->update([
                        'verify_status' => 1,
                        'verify_time'   => $now,
                        'verifier_id'   => $verifier_id,
                        'updated_at'    => $now,
                    ]);

                // 写入核销记录
                $verifier = \think\facade\Db::name(BaseService::table('verifiers'))
                    ->where('id', $verifier_id)
                    ->find();

                \think\facade\Db::name(BaseService::table('verifications'))->insert([
                    'ticket_id'    => $ticket_id,
                    'ticket_code'  => $ticket['ticket_code'],
                    'verifier_id'  => $verifier_id,
                    'verifier_name'=> $verifier['name'] ?? '',
                    'goods_id'     => $ticket['goods_id'],
                    'created_at'   => $now,
                ]);

                BaseService::log('verifyTicketById: success', [
                    'ticket_id'   => $ticket_id,
                    'verifier_id' => $verifier_id,
                ]);

                // 审计日志
                AuditService::logVerify(
                    $ticket_id,
                    $ticket['ticket_code'],
                    $verifier_id,
                    $verifier['name'] ?? '',
                    'success',
                    0
                );

                return [
                    'code' => 0,
                    'msg'  => '核销成功',
                    'data' => [
                        'seat_info'  => $ticket['seat_info'],
                        'real_name'  => $ticket['real_name'],
                        'goods_name' => json_decode($ticket['goods_snapshot'] ?? '{}', true)['goods_name'] ?? '',
                    ],
                ];
            });
        } catch (\Throwable $e) {
            BaseService::log('verifyTicketById: transaction_error', [
                'ticket_id' => $ticket_id,
                'error'     => $e->getMessage(),
            ], 'error');
            return ['code' => -999, 'msg' => '核销失败，请重试'];
        }
    }

    /**
     * 生成 QR 码图片 URL
     */
    public static function getQrCodeUrl($ticket_code)
    {
        $content = base64_encode(json_encode([
            'type' => 'vr_ticket',
            'code' => $ticket_code,
        ]));
        return request()->domain() . request()->root() . '?s=index/qrcode/index&content=' . urlencode($content) . '&size=8&level=H';
    }

    /**
     * 获取票的 QR 数据（短码 + payload）
     *
     * qr_data 格式：短码|payload
     *
     * @param int $ticket_id 票ID
     * @param int $user_id 用户ID（校验归属）
     * @return array [code, data]
     */
    public static function getQrData($ticket_id, $user_id)
    {
        $ticket = \think\facade\Db::name(BaseService::table('tickets'))
            ->where('id', $ticket_id)
            ->where('user_id', $user_id)
            ->find();

        if (empty($ticket)) {
            return ['code' => -1, 'msg' => '票不存在'];
        }

        // 已核销的票不返回 QR
        if ($ticket['verify_status'] == 1) {
            return ['code' => -2, 'msg' => '该票已核销'];
        }

        // 已退款的票不返回 QR
        if ($ticket['verify_status'] == 2) {
            return ['code' => -3, 'msg' => '该票已退款'];
        }

        $qr_data = $ticket['qr_data'] ?? '';
        if (empty($qr_data) || strpos($qr_data, '|') === false) {
            return ['code' => -4, 'msg' => 'QR数据异常'];
        }

        [$short_code, $payload] = explode('|', $qr_data, 2);

        // 检查是否需要刷新 QR（剩余有效期 < 15分钟）
        if (!empty($payload)) {
            $decoded = BaseService::verifyQrPayload($payload);
            if ($decoded !== null && $decoded['exp'] - time() > 900) {
                // 有效期 > 15分钟，返回缓存
                // 签名验证（包含 ticket_code）
                $secret = self::getVrSecret();
                $sign_str = $decoded['code'] . '.' . $decoded['id'] . '.' . $decoded['g'] . '.' . $decoded['iat'] . '.' . $decoded['exp'];
                $expected_sig = substr(hash_hmac('sha256', $sign_str, $secret), 0, 8);
                if (isset($decoded['sig']) && $decoded['sig'] === $expected_sig) {
                    return [
                        'code' => 0,
                        'msg'  => 'success',
                        'data' => [
                            'short_code'  => $short_code,
                            'payload'     => $payload,
                            'cached'      => true,
                            'expires_in'  => $decoded['exp'] - time(),
                        ],
                    ];
                }
            }
        }

        // 需要刷新 QR（过期或即将过期）
        $now = time();
        $new_payload = BaseService::signQrPayload([
            'id'   => $ticket_id,
            'g'    => $ticket['goods_id'],
            'iat'  => $now,
            'exp'  => $now + 1800, // 30分钟
        ]);

        // 更新缓存
        $new_qr_data = $short_code . '|' . $new_payload;
        \think\facade\Db::name(BaseService::table('tickets'))
            ->where('id', $ticket_id)
            ->update(['qr_data' => $new_qr_data, 'updated_at' => $now]);

        return [
            'code' => 0,
            'msg'  => 'success',
            'data' => [
                'short_code'  => $short_code,
                'payload'     => $new_payload,
                'cached'      => false,
                'expires_in'  => 1800,
            ],
        ];
    }
}
