<?php


namespace app\listener\chat;

use app\services\chat\MessageService;
use app\services\chat\SessionService;
use app\models\chat\ChatMessage;
use app\models\chat\ChatSession;
use think\facade\Db;
use think\swoole\WebSocket;

/**
 * 聊天消息监听器
 * 处理: chat.message.send, chat.message.ack, chat.typing
 * Class ChatMessageListener
 * @package app\listener\chat
 */
class ChatMessageListener
{
    private $messageService;
    private $sessionService;

    public function __construct()
    {
        $this->messageService = new MessageService();
        $this->sessionService = new SessionService();
    }

    /**
     * 处理发送消息
     * 两段ACK: server_ack(服务端已接收) + peer_ack(对端已送达)
     */
    public function onMessageSend($ws, $event)
    {
        $fd = $ws->getSender();
        $tableFd = app('swoole.table.fd');
        $fdInfo = $tableFd->get((string)$fd);

        if (!$fdInfo || !in_array($fdInfo['mode'], ['chat_user', 'chat_agent'])) {
            $ws->emit('chat.message.server_ack', [
                'success' => false,
                'error' => 'not_connected'
            ]);
            return;
        }

        $sessionId = (int)($event['sessionId'] ?? 0);
        $msgType = (int)($event['msgType'] ?? ChatMessage::MSG_TYPE_TEXT);
        $content = $event['content'] ?? '';
        $clientMsgId = $event['clientMsgId'] ?? null;

        // 验证会话
        $session = ChatSession::find($sessionId);
        if (!$session || $session['status'] === ChatSession::STATUS_ENDED) {
            $ws->emit('chat.message.server_ack', [
                'clientMsgId' => $clientMsgId,
                'success' => false,
                'error' => 'session_invalid'
            ]);
            return;
        }

        // 验证内容
        $validation = $this->messageService->validateContent($content, $msgType);
        if (!$validation['valid']) {
            $ws->emit('chat.message.server_ack', [
                'clientMsgId' => $clientMsgId,
                'success' => false,
                'error' => $validation['error']
            ]);
            return;
        }

        // 确定发送者
        $senderType = $fdInfo['mode'] === 'chat_user'
            ? ChatMessage::SENDER_USER
            : ChatMessage::SENDER_ADMIN;
        $senderId = $fdInfo['user_id'];

        // 验证发送者权限
        if ($senderType === ChatMessage::SENDER_USER && $session['user_id'] != $senderId) {
            $ws->emit('chat.message.server_ack', [
                'clientMsgId' => $clientMsgId,
                'success' => false,
                'error' => 'permission_denied'
            ]);
            return;
        }
        if ($senderType === ChatMessage::SENDER_ADMIN && $session['admin_id'] != $senderId) {
            $ws->emit('chat.message.server_ack', [
                'clientMsgId' => $clientMsgId,
                'success' => false,
                'error' => 'permission_denied'
            ]);
            return;
        }

        // 创建消息
        $message = $this->messageService->createMessage([
            'session_id' => $sessionId,
            'sender_type' => $senderType,
            'sender_id' => $senderId,
            'msg_type' => $msgType,
            'content' => $content,
            'client_msg_id' => $clientMsgId,
        ]);

        // 发送 server_ack
        $ws->emit('chat.message.server_ack', [
            'clientMsgId' => $clientMsgId,
            'msgId' => $message['msg_id'],
            'success' => true,
            'status' => 'received'
        ]);

        // 推送给对端
        $this->pushToPeer($ws, $session->toArray(), $message, $senderType);
    }

    /**
     * 处理消息确认 (已读回执)
     */
    public function onMessageAck($ws, $event)
    {
        $msgId = $event['msgId'] ?? null;
        $msgIds = $event['msgIds'] ?? [];

        if ($msgId) {
            $msgIds[] = $msgId;
        }

        if (empty($msgIds)) {
            return;
        }

        // 标记为已读
        $this->messageService->markMessagesAsRead($msgIds);

        // 通知发送方消息已读
        $fd = $ws->getSender();
        $tableFd = app('swoole.table.fd');
        $fdInfo = $tableFd->get((string)$fd);

        if (!$fdInfo) {
            return;
        }

        // 获取消息信息，通知原发送者
        foreach ($msgIds as $id) {
            $message = ChatMessage::where('msg_id', $id)->find();
            if (!$message) {
                continue;
            }

            $session = ChatSession::find($message['session_id']);
            if (!$session) {
                continue;
            }

            // 确定对端
            if ($message['sender_type'] === ChatMessage::SENDER_USER) {
                $targetFd = $this->sessionService->getUserFd($session['user_id']);
            } else {
                $targetFd = $this->sessionService->getAgentFd($session['admin_id']);
            }

            if ($targetFd) {
                $server = app('swoole.server');
                if ($server->isEstablished($targetFd)) {
                    $server->push($targetFd, json_encode([
                        'event' => 'chat.message.peer_ack',
                        'data' => [
                            'msgId' => $id,
                            'status' => 'read'
                        ]
                    ]));
                }
            }
        }
    }

    /**
     * 处理正在输入状态
     */
    public function onTyping($ws, $event)
    {
        $fd = $ws->getSender();
        $tableFd = app('swoole.table.fd');
        $fdInfo = $tableFd->get((string)$fd);

        if (!$fdInfo || !in_array($fdInfo['mode'], ['chat_user', 'chat_agent'])) {
            return;
        }

        $sessionId = (int)($event['sessionId'] ?? 0);
        $isTyping = (bool)($event['isTyping'] ?? false);

        $session = ChatSession::find($sessionId);
        if (!$session || $session['status'] !== ChatSession::STATUS_ACTIVE) {
            return;
        }

        // 转发给对端
        if ($fdInfo['mode'] === 'chat_user') {
            // 用户输入，通知客服
            if ($session['admin_id']) {
                $ws->to('chat_agent_' . $session['admin_id'])->emit('chat.typing', [
                    'sessionId' => $sessionId,
                    'isTyping' => $isTyping,
                    'from' => 'user'
                ]);
            }
        } else {
            // 客服输入，通知用户
            $ws->to('chat_user_' . $session['user_id'])->emit('chat.typing', [
                'sessionId' => $sessionId,
                'isTyping' => $isTyping,
                'from' => 'agent'
            ]);
        }
    }

    /**
     * 推送消息给对端
     */
    private function pushToPeer($ws, $session, $message, $senderType)
    {
        $payload = [
            'event' => 'chat.message.new',
            'data' => [
                'msgId' => $message['msg_id'],
                'sessionId' => $message['session_id'],
                'senderType' => $message['sender_type'],
                'msgType' => $message['msg_type'],
                'content' => $message['content'],
                'time' => $message['create_time'],
            ]
        ];

        if ($senderType === ChatMessage::SENDER_USER) {
            // 用户发送，推给客服
            if ($session['admin_id']) {
                $targetFd = $this->sessionService->getAgentFd($session['admin_id']);
                if ($targetFd) {
                    $pushed = $this->messageService->pushMessage(
                        $message['msg_id'],
                        $targetFd,
                        $payload
                    );
                    if ($pushed) {
                        // 发送 peer_ack 给发送者
                        $ws->emit('chat.message.peer_ack', [
                            'msgId' => $message['msg_id'],
                            'status' => 'delivered'
                        ]);
                    }
                }
            } else {
                // 无客服，消息保持pending状态
                $this->messageService->updateMessageStatus(
                    $message['msg_id'],
                    ChatMessage::STATUS_PENDING
                );
            }
        } else {
            // 客服发送，推给用户
            $targetFd = $this->sessionService->getUserFd($session['user_id']);
            if ($targetFd) {
                $pushed = $this->messageService->pushMessage(
                    $message['msg_id'],
                    $targetFd,
                    $payload
                );
                if ($pushed) {
                    $ws->emit('chat.message.peer_ack', [
                        'msgId' => $message['msg_id'],
                        'status' => 'delivered'
                    ]);
                }
            } else {
                // 用户离线，消息保持pending
                $this->messageService->updateMessageStatus(
                    $message['msg_id'],
                    ChatMessage::STATUS_PENDING
                );
            }
        }
    }
}
