<?php

namespace XZD\AI\V5\Agents;

use XZD\AI\V5\Core\Logger;

/**
 * 会话管理器 — AI 对话会话与消息（零持久化 / 瞬态存储）
 *
 * V3.2: 完全移除数据库持久化。
 * - 游客：使用访客令牌（guest token）隔离，关浏览器即清除。
 * - 登录用户：使用 user_id 隔离，可选是否持久化（默认不持久）。
 * - 所有数据存 WordPress Transient，TTL 30 分钟，活跃时自动续期。
 * - 所有权校验：每个操作都验证 session 归属。
 */
class SessionManager
{
    private const TTL = 1800;        // 30 分钟
    private const MAX_SESSIONS = 20; // 每人最多保留会话数

    // —— 内部 helpers ——

    private function sessKey(string $sessionKey): string
    {
        return "xzd_chat_sess_{$sessionKey}";
    }

    private function msgsKey(string $sessionKey): string
    {
        return "xzd_chat_msgs_{$sessionKey}";
    }

    private function idxKey(int $userId, string $guestToken = ''): string
    {
        if ($userId > 0) {
            return "xzd_chat_idx_u_{$userId}";
        }
        return "xzd_chat_idx_g_" . md5($guestToken);
    }

    /**
     * 生成访客令牌（UUID v4）
     */
    public static function generateGuestToken(): string
    {
        return sprintf(
            '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
            random_int(0, 0xffff), random_int(0, 0xffff),
            random_int(0, 0xffff),
            random_int(0, 0x0fff) | 0x4000,
            random_int(0, 0x3fff) | 0x8000,
            random_int(0, 0xffff), random_int(0, 0xffff), random_int(0, 0xffff)
        );
    }

    /**
     * 生成会话 key
     */
    private function generateKey(): string
    {
        return 'sk_' . bin2hex(random_bytes(16));
    }

    /**
     * 校验所有权 — 返回 session 数组或 null
     */
    private function requireOwnership(string $sessionKey, int $userId, string $guestToken = ''): ?array
    {
        $session = get_transient($this->sessKey($sessionKey));
        if (!$session || !is_array($session)) {
            return null;
        }
        if ($userId > 0) {
            if (((int) ($session['user_id'] ?? 0)) !== $userId) {
                return null;
            }
        } else {
            if (($session['guest_token'] ?? '') !== $guestToken || $guestToken === '') {
                return null;
            }
        }
        return $session;
    }

    // —— 会话 CRUD ——

    /**
     * 创建新会话
     *
     * @param int    $userId      用户ID（游客为0）
     * @param string $guestToken  访客令牌（游客必须传入）
     * @param string $title       会话标题
     * @param string $model       模型名
     * @param string $provider    提供商
     * @return array
     */
    public function createSession(int $userId, string $guestToken = '', string $title = '', string $model = '', string $provider = ''): array
    {
        $sessionKey = $this->generateKey();
        $now = current_time('mysql');

        $session = [
            'session_key' => $sessionKey,
            'user_id'     => $userId,
            'guest_token' => $guestToken,
            'title'       => $title ?: '新对话',
            'model'       => $model,
            'provider'    => $provider,
            'status'      => 'active',
            'message_count' => 0,
            'total_tokens'  => 0,
            'total_cost'    => 0,
            'created_at'  => $now,
            'updated_at'  => $now,
        ];

        set_transient($this->sessKey($sessionKey), $session, self::TTL);

        // 维护索引（最近会话列表）
        $index = get_transient($this->idxKey($userId, $guestToken));
        if (!is_array($index)) {
            $index = [];
        }
        array_unshift($index, $sessionKey);
        $index = array_slice(array_unique($index), 0, self::MAX_SESSIONS);
        set_transient($this->idxKey($userId, $guestToken), $index, self::TTL);

        Logger::info('session_create', $sessionKey, ['user_id' => $userId, 'guest' => $guestToken !== '']);

        return [
            'success'     => true,
            'session_key' => $sessionKey,
            'id'          => 0, // 不再有自增ID
        ];
    }

    /**
     * 获取会话详情（含所有权校验）
     *
     * @param string $sessionKey
     * @param int    $userId
     * @param string $guestToken
     * @return array|null
     */
    public function getSession(string $sessionKey, int $userId = 0, string $guestToken = ''): ?array
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            return null;
        }
        // 续期
        set_transient($this->sessKey($sessionKey), $session, self::TTL);
        return $session;
    }

    /**
     * 列出用户/访客的所有会话
     *
     * @param int    $userId
     * @param string $guestToken
     * @param int    $limit
     * @param int    $offset
     * @return array
     */
    public function listSessions(int $userId, string $guestToken = '', int $limit = 20, int $offset = 0): array
    {
        $index = get_transient($this->idxKey($userId, $guestToken));
        if (!is_array($index)) {
            return [];
        }

        $sessions = [];
        foreach ($index as $sessionKey) {
            $s = get_transient($this->sessKey($sessionKey));
            if ($s && is_array($s) && ($s['status'] ?? '') !== 'deleted') {
                $sessions[] = $s;
            }
        }

        // 按 updated_at 降序
        usort($sessions, fn($a, $b) => strcmp($b['updated_at'] ?? '', $a['updated_at'] ?? ''));

        return array_slice($sessions, $offset, $limit);
    }

    /**
     * 更新会话
     *
     * @param string $sessionKey
     * @param array  $data
     * @param int    $userId
     * @param string $guestToken
     * @return bool
     */
    public function updateSession(string $sessionKey, array $data, int $userId = 0, string $guestToken = ''): bool
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            return false;
        }
        $data['updated_at'] = current_time('mysql');
        $merged = array_merge($session, $data);
        return set_transient($this->sessKey($sessionKey), $merged, self::TTL);
    }

    /**
     * 软删除会话
     *
     * @param string $sessionKey
     * @param int    $userId
     * @param string $guestToken
     * @return bool
     */
    public function deleteSession(string $sessionKey, int $userId = 0, string $guestToken = ''): bool
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            return false;
        }
        delete_transient($this->sessKey($sessionKey));
        delete_transient($this->msgsKey($sessionKey));
        return true;
    }

    /**
     * 更新会话摘要信息
     *
     * @param string $sessionKey
     * @param array  $meta
     * @param int    $userId
     * @param string $guestToken
     */
    public function touchSession(string $sessionKey, array $meta, int $userId = 0, string $guestToken = ''): void
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            return;
        }

        if (isset($meta['title'])) {
            $session['title'] = $meta['title'];
        }
        if (isset($meta['model'])) {
            $session['model'] = $meta['model'];
        }
        if (isset($meta['provider'])) {
            $session['provider'] = $meta['provider'];
        }
        if (isset($meta['tokens'])) {
            $session['total_tokens'] = ($session['total_tokens'] ?? 0) + $meta['tokens'];
        }
        if (isset($meta['cost'])) {
            $session['total_cost'] = ($session['total_cost'] ?? 0) + $meta['cost'];
        }
        $session['message_count'] = ($session['message_count'] ?? 0) + 1;
        $session['updated_at'] = current_time('mysql');

        set_transient($this->sessKey($sessionKey), $session, self::TTL);
    }

    // —— 消息 ——

    /**
     * 添加消息到会话
     *
     * @param string $sessionKey
     * @param string $role
     * @param string $content
     * @param array  $meta
     * @param int    $userId
     * @param string $guestToken
     * @return int 消息索引（非DB ID）
     */
    public function addMessage(string $sessionKey, string $role, string $content, array $meta = [], int $userId = 0, string $guestToken = ''): int
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            Logger::error('message_add', 'session not found or access denied', ['session_key' => $sessionKey]);
            return 0;
        }

        $messages = get_transient($this->msgsKey($sessionKey));
        if (!is_array($messages)) {
            $messages = [];
        }

        $msg = [
            'id'            => count($messages) + 1,
            'role'          => $role,
            'content'       => $content,
            'tool_calls'    => !empty($meta['tool_calls']) ? $meta['tool_calls'] : null,
            'tool_call_id'  => $meta['tool_call_id'] ?? null,
            'model'         => $meta['model'] ?? '',
            'input_tokens'  => $meta['input_tokens'] ?? 0,
            'output_tokens' => $meta['output_tokens'] ?? 0,
            'duration_ms'   => $meta['duration_ms'] ?? 0,
            'rating'        => 0,
            'created_at'    => current_time('mysql'),
        ];

        $messages[] = $msg;
        set_transient($this->msgsKey($sessionKey), $messages, self::TTL);

        // 续期 session
        set_transient($this->sessKey($sessionKey), $session, self::TTL);

        return count($messages);
    }

    /**
     * 获取会话的所有消息
     *
     * @param string $sessionKey
     * @param int    $limit
     * @param int    $userId
     * @param string $guestToken
     * @return array
     */
    public function getMessages(string $sessionKey, int $limit = 100, int $userId = 0, string $guestToken = ''): array
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            return [];
        }

        $messages = get_transient($this->msgsKey($sessionKey));
        if (!is_array($messages)) {
            return [];
        }

        // 续期
        set_transient($this->msgsKey($sessionKey), $messages, self::TTL);
        set_transient($this->sessKey($sessionKey), $session, self::TTL);

        if (count($messages) <= $limit) {
            return $messages;
        }
        return array_slice($messages, -$limit);
    }

    /**
     * 获取最近 N 条消息（用于构建 LLM 上下文）
     *
     * @param string $sessionKey
     * @param int    $count
     * @param int    $userId
     * @param string $guestToken
     * @return array
     */
    public function getRecentMessages(string $sessionKey, int $count = 20, int $userId = 0, string $guestToken = ''): array
    {
        $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
        if (!$session) {
            Logger::error('message_get_recent', 'session not found or access denied', ['session_key' => $sessionKey]);
            return [];
        }

        $messages = get_transient($this->msgsKey($sessionKey));
        if (!is_array($messages)) {
            return [];
        }

        // 续期
        set_transient($this->msgsKey($sessionKey), $messages, self::TTL);

        // 取最近 N 条非 system 消息
        $filtered = array_filter($messages, fn($m) => ($m['role'] ?? '') !== 'system');
        $rows = array_slice(array_values($filtered), -$count);

        // 反转为时间正序
        $rows = array_reverse($rows);

        // 补全：如果第一条消息是 tool 角色，补 assistant(tool_calls)
        if (!empty($rows) && $rows[0]['role'] === 'tool' && isset($rows[0]['id'])) {
            $firstId = (int) $rows[0]['id'];
            $prevRows = [];
            foreach (array_reverse($messages) as $m) {
                if ((int) ($m['id'] ?? 0) < $firstId && $m['role'] === 'assistant' && !empty($m['tool_calls'])) {
                    $prevRows[] = $m;
                    if (count($prevRows) >= 5) break;
                }
            }
            if (!empty($prevRows)) {
                $prevRows = array_reverse($prevRows);
                array_splice($rows, 0, 0, $prevRows);
            }
        }

        // 构造 LLM 消息格式
        $msgs = [];
        foreach ($rows as $row) {
            $msg = ['role' => $row['role'], 'content' => $row['content']];
            if (!empty($row['tool_calls'])) {
                $tc = $row['tool_calls'];
                $msg['tool_calls'] = is_array($tc) ? $tc : json_decode($tc, true);
            }
            if (!empty($row['tool_call_id'])) {
                $msg['tool_call_id'] = $row['tool_call_id'];
            }
            $msgs[] = $msg;
        }

        // 完整性校验：移除无法配对的 tool_calls
        for ($i = 0; $i < count($msgs); $i++) {
            if (!isset($msgs[$i]['tool_calls']) || empty($msgs[$i]['tool_calls'])) {
                continue;
            }
            $requiredIds = [];
            foreach ($msgs[$i]['tool_calls'] as $tc) {
                if (!empty($tc['id'])) {
                    $requiredIds[$tc['id']] = true;
                }
            }
            for ($j = $i + 1; $j < count($msgs); $j++) {
                if ($msgs[$j]['role'] === 'tool' && isset($requiredIds[$msgs[$j]['tool_call_id'] ?? ''])) {
                    unset($requiredIds[$msgs[$j]['tool_call_id']]);
                }
                if ($msgs[$j]['role'] !== 'tool') break;
            }
            if (!empty($requiredIds)) {
                $msgs[$i]['tool_calls'] = array_values(array_filter(
                    $msgs[$i]['tool_calls'],
                    fn($tc) => !isset($requiredIds[$tc['id'] ?? ''])
                ));
                if (empty($msgs[$i]['tool_calls'])) {
                    unset($msgs[$i]['tool_calls']);
                }
            }
        }

        // 反向校验：移除孤立的 tool 消息
        foreach ($msgs as $i => $msg) {
            if (($msg['role'] ?? '') !== 'tool' || empty($msg['tool_call_id'] ?? '')) {
                continue;
            }
            $hasMatchingAssistant = false;
            for ($j = $i - 1; $j >= 0; $j--) {
                if (($msgs[$j]['role'] ?? '') !== 'assistant') continue;
                if (empty($msgs[$j]['tool_calls'])) continue;
                foreach ($msgs[$j]['tool_calls'] as $tc) {
                    if (($tc['id'] ?? '') === $msg['tool_call_id']) {
                        $hasMatchingAssistant = true;
                        break 2;
                    }
                }
                break;
            }
            if (!$hasMatchingAssistant) {
                unset($msgs[$i]);
            }
        }
        $msgs = array_values($msgs);

        return $msgs;
    }

    /**
     * 评价消息
     *
     * @param int    $messageId  消息在数组中的ID
     * @param int    $rating     评分
     * @param string $sessionKey
     * @param int    $userId
     * @param string $guestToken
     * @return bool
     */
    public function rateMessage(int $messageId, int $rating, string $sessionKey = '', int $userId = 0, string $guestToken = ''): bool
    {
        if ($sessionKey) {
            $session = $this->requireOwnership($sessionKey, $userId, $guestToken);
            if (!$session) {
                return false;
            }
        }

        // 从 sessionKey 的 transient 中找到对应消息并标记
        if ($sessionKey) {
            $messages = get_transient($this->msgsKey($sessionKey));
            if (is_array($messages)) {
                foreach ($messages as &$m) {
                    if ((int) ($m['id'] ?? 0) === $messageId) {
                        $m['rating'] = $rating;
                        set_transient($this->msgsKey($sessionKey), $messages, self::TTL);
                        return true;
                    }
                }
            }
        }

        return false;
    }

    // —— 会话统计 ——

    /**
     * 获取用户/访客会话统计
     *
     * @param int    $userId
     * @param string $guestToken
     * @return array
     */
    public function getUserStats(int $userId, string $guestToken = ''): array
    {
        $index = get_transient($this->idxKey($userId, $guestToken));
        if (!is_array($index)) {
            return ['total_sessions' => 0, 'total_messages' => 0];
        }

        $totalMessages = 0;
        $activeSessions = 0;
        foreach ($index as $sessionKey) {
            $s = get_transient($this->sessKey($sessionKey));
            if ($s && is_array($s) && ($s['status'] ?? '') === 'active') {
                $activeSessions++;
            }
            $msgs = get_transient($this->msgsKey($sessionKey));
            if (is_array($msgs)) {
                $totalMessages += count($msgs);
            }
        }

        return [
            'total_sessions' => $activeSessions,
            'total_messages' => $totalMessages,
        ];
    }

    /**
     * 检查所需数据库表是否存在（保留兼容）
     *
     * @return bool
     */
    public function hasTables(): bool
    {
        // V3.2: 不再使用数据库表，始终返回 true
        return true;
    }

    /**
     * 清理过期数据（cron 调用）
     */
    public static function cleanupExpired(): void
    {
        // Transient 自动过期，此方法保留用于未来扩展
        // 可以在此处做索引清理等维护工作
    }
}
