package com.tsmobile.app.data

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

/** 共享 Json 实例，避免 parseFileMessageMeta 每次调用都创建新实例 */
private val fileJson = Json { ignoreUnknownKeys = true }

/**
 * 服务器连接配置。
 * 用于 ViewModel 状态和最近连接列表持久化。
 */
@Serializable
data class ServerConfig(
    val address: String = "",       // 服务器地址（域名/IP/TSDNS）
    val nickname: String = "",      // 昵称
    val password: String = "",      // 服务器密码（可选）
    val defaultChannel: String = "",         // 默认频道（可选）
    val defaultChannelPassword: String = "", // 默认频道密码（可选）
)

/**
 * 最近连接记录。
 * 点击可快速连接（复用 address/nickname/password）。
 */
@Serializable
data class RecentConnection(
    val address: String,
    val nickname: String,
    val password: String = "",
    val lastConnectedAt: Long = 0L,  // 最后连接时间戳（epoch ms）
    val lastSucceeded: Boolean = false, // 上次连接是否成功
)

/**
 * 频道信息。
 * 用于频道列表显示和状态管理。
 */
@Serializable
data class ChannelInfo(
    val id: String = "",           // 频道 ID（字符串化）
    val parentId: String = "0",    // 父频道 ID
    val name: String = "",         // 频道名称
    val description: String = "",  // 频道描述
    val order: Int = 0,            // 排序
    val isPassword: Boolean = false,  // 是否需要密码
    val isPermanent: Boolean = false, // 是否永久频道
    val maxClients: Int = -1,      // 最大用户数（-1=无限制）
)

/**
 * 客户端信息。
 * 用于成员列表显示和状态管理。
 */
@Serializable
data class ClientInfo(
    val id: Int = 0,               // 客户端 ID
    val nickname: String = "",     // 昵称
    val uid: String = "",          // 唯一标识
    val channelId: String = "",    // 所在频道 ID
    val serverGroups: List<String> = emptyList(), // 服务器组
    val isSelf: Boolean = false,   // 是否是自己
)

/**
 * 消息发送状态。
 *
 * PENDING：已发送到本地，等待服务端回推确认
 * SENT：服务端已确认（收到 OnTextMessage 回显）
 * FAILED：发送失败（命令被拒绝或超时未确认）
 */
enum class MessageDeliveryState {
    PENDING,
    SENT,
    FAILED
}

/**
 * 文件消息元数据（从 JSON content 中解析）。
 * 仅当 msg_type 为文件类型时填充。
 */
data class FileMessageMeta(
    val msgType: String,       // 消息类型，如 "ts.file.myts"
    val fileId: String,        // 文件 ID（base64 编码）
    val fileName: String,      // 文件名
    val fileSize: Long,        // 文件大小（字节）
    val chatUserId: String,    // MyTS 聊天用户 ID
) {
    /** 是否是图片文件 */
    val isImage: Boolean
        get() = fileName.matches(Regex(".*\\.(png|jpg|jpeg|gif|webp|bmp)$", RegexOption.IGNORE_CASE))

    /** 是否是 MyTS 文件（存储在独立服务器，无法通过标准文件传输下载） */
    val isMyTS: Boolean
        get() = msgType == "ts.file.myts"

    /** 格式化的文件大小 */
    val formattedSize: String
        get() = when {
            fileSize < 1024 -> "$fileSize B"
            fileSize < 1024 * 1024 -> "${fileSize / 1024} KB"
            else -> "${"%.1f".format(fileSize / (1024.0 * 1024.0))} MB"
        }
}

/**
 * 消息类型。
 */
enum class MessageType { NORMAL, SYSTEM }

/**
 * 聊天消息实体。
 * 权威来源：OnTextMessage（对应 notifytextmessage）。
 */
data class ChatMessage(
    val id: String,            // 唯一标识（用于列表 key）
    val targetMode: Int,       // 1=私聊, 2=频道, 3=服务器
    val targetId: Long,        // 目标 ID（频道 ID 或客户端 ID）
    val senderId: Int,         // 发送者客户端 ID
    val senderName: String,    // 发送者昵称
    val content: String,       // 消息内容（纯文本或原始 JSON）
    val timestamp: Long,       // 消息时间戳
    val isSelf: Boolean,       // 是否是自己发送的
    val deliveryState: MessageDeliveryState = MessageDeliveryState.SENT, // 送达状态
    val fileMeta: FileMessageMeta? = null, // 文件消息元数据（仅文件消息）
    val messageType: MessageType = MessageType.NORMAL, // 消息类型
)

/**
 * 消息发送状态机。
 *
 * 状态转换：
 *   Idle → Sending：用户提交发送意图
 *   Sending → Idle：命令成功（等待 OnTextMessage 归档）
 *   Sending → Failed：命令被拒绝
 *   Failed → Sending：用户重试
 */
sealed class MessageSendState {
    /** 空闲，可以发送新消息 */
    data class Idle(val justSent: Boolean = false) : MessageSendState()

    /** 正在发送 SendTextMessage 等待响应 */
    object Sending : MessageSendState()

    /** 发送失败 */
    data class Failed(val error: String) : MessageSendState()
}

/**
 * 连接状态机。
 *
 * 状态转换：
 *   Connected → Disconnecting：主动断开
 *   Connected → Reconnecting：网络异常
 *   Connected → Disconnected(wasKicked=true)：被踢
 *   Reconnecting → Connected：重连成功
 *   Reconnecting → Disconnected：重连失败/用户放弃
 *   Disconnecting → Disconnected：断开完成
 */
sealed class ConnectionState {
    /** 已连接 — 正常会话中 */
    object Connected : ConnectionState()

    /** 断开中 — 正在主动断开 */
    object Disconnecting : ConnectionState()

    /** 重连中 — 网络异常后自动重连 */
    data class Reconnecting(
        val attempt: Int,
        val maxAttempts: Int = 5,
        val reason: String = ""
    ) : ConnectionState()

    /** 等待网络恢复 — 网络丢失后等待连接恢复 */
    data class WaitingForNetwork(
        val since: Long = System.currentTimeMillis()
    ) : ConnectionState()

    /** 已断开 — 会话结束 */
    data class Disconnected(
        val reason: String = "",
        val wasKicked: Boolean = false
    ) : ConnectionState()
}

/**
 * 语音发送状态机。
 *
 * 状态转换：
 *   Idle → Transmitting：用户按下 PTT 且前置条件满足
 *   Idle → Blocked：用户按下 PTT 但前置条件不满足
 *   Transmitting → Idle：用户松开 PTT
 *   Transmitting → Blocked：连接断开或被踢
 *   Blocked → Idle：重连成功或用户手动恢复
 */
sealed class VoiceState {
    /** 静默 — 没有发送语音帧 */
    object Idle : VoiceState()

    /** 发送中 — 正在采集、编码并发送 Opus 帧 */
    object Transmitting : VoiceState()

    /** 受阻 — 未连接、采集失败或发送异常 */
    data class Blocked(val reason: String) : VoiceState()
}

/**
 * 服务器信息。
 * 对应 bridge.go serverInfoJSON
 */
@Serializable
data class ServerInfo(
    val name: String = "",
    val welcomeMessage: String = "",
    val maxClients: Int = 0,
    val clientsOnline: Int = 0,
    val channelsOnline: Int = 0,
    val uptime: String = "",       // 运行时长（秒，字符串化）
    val version: String = "",
    val platform: String = "",
    val created: String = "",      // 创建时间戳（秒，字符串化）
    val iconId: String = "",
    val defaultServerGroup: Int = 0,
    val defaultChannelGroup: Int = 0,
)

/**
 * 频道详细信息。
 * 对应 bridge.go channelDetailedInfoJSON
 */
@Serializable
data class ChannelDetailInfo(
    val id: String = "",
    val parentId: String = "",
    val name: String = "",
    val topic: String = "",
    val description: String = "",
    val codec: Int = 0,
    val codecQuality: Int = 0,
    val maxClients: Int = 0,
    val maxFamilyClients: Int = 0,
    val neededTalkPower: Int = 0,
    val iconId: String = "",
    val isPermanent: Boolean = false,
    val isSemiPermanent: Boolean = false,
    val isDefault: Boolean = false,
    val isPassword: Boolean = false,
    val order: String = "",
    val bannerGfxUrl: String = "",
)

/**
 * Poke 事件。
 * 对应 bridge.go PokeEvent
 */
data class PokeEvent(
    val invokerID: Int = 0,
    val invokerName: String = "",
    val invokerUID: String = "",
    val message: String = "",
)

/**
 * 语音输出设备。
 */
enum class VoiceOutputDevice {
    SPEAKER,    // 扬声器
    EARPIECE    // 听筒
}

/**
 * 尝试从消息内容中解析文件消息元数据。
 * TeamSpeak MyTS 文件消息的 content 为 JSON 格式，包含 msg_type、file_id 等字段。
 * 普通文本消息返回 null。
 */
fun parseFileMessageMeta(content: String): FileMessageMeta? {
    if (!content.trimStart().startsWith("{")) return null
    return try {
        val obj = fileJson.decodeFromString<kotlinx.serialization.json.JsonObject>(content)
        val msgType = obj["msg_type"]?.toString()?.trim('"') ?: return null
        if (!msgType.startsWith("ts.file")) return null
        val meta = FileMessageMeta(
            msgType = msgType,
            fileId = obj["file_id"]?.toString()?.trim('"') ?: "",
            fileName = obj["file_name"]?.toString()?.trim('"') ?: obj["body"]?.toString()?.trim('"') ?: "",
            fileSize = obj["file_size"]?.toString()?.trim('"')?.toLongOrNull() ?: 0L,
            chatUserId = obj["chat_user_id"]?.toString()?.trim('"') ?: "",
        )
        android.util.Log.d("parseFileMessageMeta", "Parsed: msgType=${meta.msgType}, fileName=${meta.fileName}, isImage=${meta.isImage}, isMyTS=${meta.isMyTS}")
        meta
    } catch (e: Exception) {
        android.util.Log.e("parseFileMessageMeta", "Parse error: ${e.message}, content=${content.take(100)}")
        null
    }
}
