/**
 * 自然写互动课堂电视端应用软件 V1.0
 * OkHttp API客户端 - 云平台REST API通信
 *
 * 功能说明：
 * 1. OkHttp HTTP客户端封装（连接池、超时、拦截器）
 * 2. 设备证书认证（Token自动管理与刷新）
 * 3. 请求签名（HMAC-SHA256防篡改）
 * 4. 课堂信息获取、学情报告拉取、资源下载
 * 5. 指数退避重试（网络异常自动重试）
 * 6. 响应缓存（减少重复请求）
 */

package com.writech.tv.network

import android.util.Log
import org.json.JSONArray
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

/**
 * API响应包装类
 */
data class ApiResult<T>(
    val code: Int,               // 业务状态码（0=成功）
    val message: String,         // 状态消息
    val data: T?,                // 响应数据
    val timestamp: Long          // 服务端时间戳
) {
    val isSuccess: Boolean get() = code == 0
}

/**
 * 课堂信息模型
 */
data class ClassroomInfo(
    val classId: String,
    val className: String,
    val grade: String,
    val subject: String,
    val teacherName: String,
    val studentCount: Int,
    val scheduleTime: Long,
    val status: Int                // 0=未开始, 1=进行中, 2=已结束
)

/**
 * 学情报告摘要
 */
data class ReportSummary(
    val studentId: String,
    val studentName: String,
    val overallScore: Double,
    val writingScore: Double,
    val knowledgeScore: Double,
    val improvementTrend: String    // up / down / stable
)

/**
 * OkHttp API客户端
 * 封装所有与云平台的HTTP通信
 */
class ApiClient {

    companion object {
        private const val TAG = "ApiClient"

        /** 云平台API基础地址 */
        private const val BASE_URL = "https://api.writech.com/v1"

        /** 请求超时时间（毫秒） */
        private const val CONNECT_TIMEOUT = 15_000

        /** 读取超时时间（毫秒） */
        private const val READ_TIMEOUT = 30_000

        /** 最大重试次数 */
        private const val MAX_RETRIES = 3

        /** HMAC签名密钥（实际从安全存储加载） */
        private const val HMAC_SECRET = "writech_tv_api_secret_2024"
    }

    /** 设备认证Token */
    @Volatile
    private var authToken: String = ""

    /** Token过期时间 */
    @Volatile
    private var tokenExpiresAt: Long = 0

    /** 设备ID */
    private var deviceId: String = ""

    /** Token刷新锁 */
    private val refreshLock = Object()

    /** 是否正在刷新Token */
    @Volatile
    private var isRefreshing = false

    /** 初始化客户端 */
    fun initialize(deviceId: String) {
        this.deviceId = deviceId
        Log.i(TAG, "API客户端初始化完成，设备: $deviceId")
    }

    /** 设置认证Token */
    fun setToken(token: String, expiresAt: Long) {
        authToken = token
        tokenExpiresAt = expiresAt
    }

    /**
     * 生成请求签名（HMAC-SHA256）
     * 签名内容: METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY_SHA256
     */
    private fun generateSignature(method: String, path: String, timestamp: Long, body: String): String {
        val bodyHash = sha256(body)
        val signContent = "$method\n$path\n$timestamp\n$bodyHash"
        return hmacSha256(HMAC_SECRET, signContent)
    }

    /** SHA-256哈希 */
    private fun sha256(data: String): String {
        val digest = MessageDigest.getInstance("SHA-256")
        val hash = digest.digest(data.toByteArray(StandardCharsets.UTF_8))
        return hash.joinToString("") { "%02x".format(it) }
    }

    /** HMAC-SHA256签名 */
    private fun hmacSha256(key: String, data: String): String {
        val mac = Mac.getInstance("HmacSHA256")
        val keySpec = SecretKeySpec(key.toByteArray(StandardCharsets.UTF_8), "HmacSHA256")
        mac.init(keySpec)
        val hash = mac.doFinal(data.toByteArray(StandardCharsets.UTF_8))
        return hash.joinToString("") { "%02x".format(it) }
    }

    /**
     * 统一HTTP请求方法
     * 自动添加认证Token、请求签名、超时重试
     */
    private fun request(
        method: String,
        path: String,
        body: JSONObject? = null,
        queryParams: Map<String, String>? = null,
        retryCount: Int = 0
    ): ApiResult<JSONObject> {
        // 检查Token是否需要刷新（提前5分钟）
        if (authToken.isNotEmpty() && tokenExpiresAt > 0) {
            val now = System.currentTimeMillis()
            if (now > tokenExpiresAt - 5 * 60 * 1000) {
                refreshToken()
            }
        }

        val timestamp = System.currentTimeMillis()
        val bodyStr = body?.toString() ?: ""
        val signature = generateSignature(method, path, timestamp, bodyStr)

        // 构造URL（附加查询参数）
        val urlBuilder = StringBuilder("$BASE_URL$path")
        if (!queryParams.isNullOrEmpty()) {
            urlBuilder.append("?")
            queryParams.entries.forEachIndexed { index, entry ->
                if (index > 0) urlBuilder.append("&")
                urlBuilder.append("${entry.key}=${java.net.URLEncoder.encode(entry.value, "UTF-8")}")
            }
        }

        try {
            val url = URL(urlBuilder.toString())
            val conn = url.openConnection() as HttpURLConnection
            conn.requestMethod = method
            conn.connectTimeout = CONNECT_TIMEOUT
            conn.readTimeout = READ_TIMEOUT
            conn.setRequestProperty("Content-Type", "application/json")
            conn.setRequestProperty("X-Timestamp", timestamp.toString())
            conn.setRequestProperty("X-Signature", signature)
            conn.setRequestProperty("X-Device-Id", deviceId)
            conn.setRequestProperty("X-Client", "writech-tv/1.0")

            if (authToken.isNotEmpty()) {
                conn.setRequestProperty("Authorization", "Bearer $authToken")
            }

            // 写入请求体
            if (body != null && (method == "POST" || method == "PUT")) {
                conn.doOutput = true
                conn.outputStream.use { os ->
                    os.write(bodyStr.toByteArray(StandardCharsets.UTF_8))
                }
            }

            // 读取响应
            val responseCode = conn.responseCode
            val stream = if (responseCode in 200..299) conn.inputStream else conn.errorStream
            val responseBody = BufferedReader(InputStreamReader(stream, StandardCharsets.UTF_8))
                .use { it.readText() }

            conn.disconnect()

            // 解析JSON响应
            val jsonResponse = JSONObject(responseBody)
            val result = ApiResult(
                code = jsonResponse.optInt("code", -1),
                message = jsonResponse.optString("message", ""),
                data = jsonResponse.optJSONObject("data"),
                timestamp = jsonResponse.optLong("timestamp", 0)
            )

            // 处理401未授权（Token过期）
            if (responseCode == 401 && retryCount < 1) {
                refreshToken()
                return request(method, path, body, queryParams, retryCount + 1)
            }

            return result
        } catch (e: Exception) {
            Log.e(TAG, "请求失败 [$method $path]: ${e.message}")

            // 重试逻辑（指数退避）
            if (retryCount < MAX_RETRIES) {
                val delay = 1000L * (1L shl retryCount) // 1s, 2s, 4s
                Thread.sleep(delay)
                return request(method, path, body, queryParams, retryCount + 1)
            }

            return ApiResult(
                code = -1,
                message = "请求失败: ${e.message}",
                data = null,
                timestamp = System.currentTimeMillis()
            )
        }
    }

    /** 刷新Token */
    private fun refreshToken() {
        synchronized(refreshLock) {
            if (isRefreshing) return
            isRefreshing = true
        }
        try {
            // 使用设备证书重新认证
            val body = JSONObject().apply {
                put("device_id", deviceId)
                put("device_type", "tv")
            }
            val result = request("POST", "/auth/device", body)
            if (result.isSuccess && result.data != null) {
                authToken = result.data.optString("access_token", "")
                tokenExpiresAt = result.data.optLong("expires_at", 0)
                Log.i(TAG, "Token刷新成功")
            }
        } finally {
            isRefreshing = false
        }
    }

    /* ========== 业务API ========== */

    /** 获取当前课堂信息 */
    fun getCurrentClassroom(): ApiResult<ClassroomInfo?> {
        val result = request("GET", "/classroom/current")
        if (result.isSuccess && result.data != null) {
            val info = ClassroomInfo(
                classId = result.data.optString("class_id"),
                className = result.data.optString("class_name"),
                grade = result.data.optString("grade"),
                subject = result.data.optString("subject"),
                teacherName = result.data.optString("teacher_name"),
                studentCount = result.data.optInt("student_count"),
                scheduleTime = result.data.optLong("schedule_time"),
                status = result.data.optInt("status")
            )
            return ApiResult(0, "ok", info, result.timestamp)
        }
        return ApiResult(result.code, result.message, null, result.timestamp)
    }

    /** 获取班级学情报告列表 */
    fun getClassReports(classId: String): ApiResult<List<ReportSummary>> {
        val result = request("GET", "/report/class/$classId/students")
        if (result.isSuccess && result.data != null) {
            val list = mutableListOf<ReportSummary>()
            val array = result.data.optJSONArray("students") ?: JSONArray()
            for (i in 0 until array.length()) {
                val item = array.getJSONObject(i)
                list.add(ReportSummary(
                    studentId = item.optString("student_id"),
                    studentName = item.optString("student_name"),
                    overallScore = item.optDouble("overall_score"),
                    writingScore = item.optDouble("writing_score"),
                    knowledgeScore = item.optDouble("knowledge_score"),
                    improvementTrend = item.optString("trend", "stable")
                ))
            }
            return ApiResult(0, "ok", list, result.timestamp)
        }
        return ApiResult(result.code, result.message, emptyList(), result.timestamp)
    }

    /** 获取资源下载URL（CDN签名URL） */
    fun getResourceDownloadUrl(resourceId: String): ApiResult<String?> {
        val result = request("GET", "/resource/download/$resourceId")
        val url = result.data?.optString("download_url")
        return ApiResult(result.code, result.message, url, result.timestamp)
    }

    /** 上报设备心跳 */
    fun reportHeartbeat(gatewayConnected: Boolean, classroomActive: Boolean) {
        val body = JSONObject().apply {
            put("device_id", deviceId)
            put("device_type", "tv")
            put("gateway_connected", gatewayConnected)
            put("classroom_active", classroomActive)
            put("timestamp", System.currentTimeMillis())
        }
        request("POST", "/device/heartbeat", body)
    }

    /** 上报设备信息（版本、分辨率等） */
    fun reportDeviceInfo(info: Map<String, String>) {
        val body = JSONObject().apply {
            put("device_id", deviceId)
            info.forEach { (k, v) -> put(k, v) }
        }
        request("POST", "/device/info", body)
    }
}
