/**
 * 自然写互动课堂智慧黑板端应用软件 V1.0
 *
 * CloudApiClient.kt - 云平台API客户端
 *
 * 功能说明：
 * - JWT认证与Token自动刷新
 * - 课件资源下载
 * - 课堂数据同步
 * - 录像文件上传
 * - 设备注册与心跳
 * - 请求签名（HMAC-SHA256）
 */

package com.writech.board.network

import android.util.Log
import org.json.JSONObject
import java.io.*
import java.net.HttpURLConnection
import java.net.URL
import java.security.MessageDigest
import java.util.concurrent.*

/** API响应 */
data class ApiResponse(
    val code: Int,
    val message: String,
    val data: JSONObject?,
    val httpCode: Int = 200
) {
    val isSuccess: Boolean get() = code == 200 || code == 0
}

/** 认证令牌 */
data class AuthToken(
    val accessToken: String,
    val refreshToken: String,
    val expiresAt: Long,
    val tokenType: String = "Bearer"
)

/**
 * 云平台API客户端
 * 基于HTTPS与云平台通信，支持设备证书认证、JWT刷新、请求签名
 */
class CloudApiClient(
    private val baseUrl: String,
    private val deviceId: String
) {
    companion object {
        private const val TAG = "CloudApiClient"
        private const val CONNECT_TIMEOUT = 15000
        private const val READ_TIMEOUT = 30000
        private const val MAX_RETRIES = 3
        private const val CHUNK_SIZE = 2 * 1024 * 1024
    }

    @Volatile
    private var authToken: AuthToken? = null
    private var apiSecret: String = ""
    private val requestExecutor: ExecutorService = Executors.newFixedThreadPool(4)

    /**
     * 设备认证登录 - 使用设备证书申请JWT令牌
     */
    fun authenticate(deviceCert: String, callback: (Boolean, String) -> Unit) {
        requestExecutor.submit {
            try {
                val body = JSONObject().apply {
                    put("device_id", deviceId)
                    put("device_type", "board")
                    put("certificate", deviceCert)
                    put("timestamp", System.currentTimeMillis())
                }
                val response = doPost("/api/v1/auth/device-login", body.toString())
                if (response.isSuccess && response.data != null) {
                    authToken = AuthToken(
                        accessToken = response.data.getString("access_token"),
                        refreshToken = response.data.getString("refresh_token"),
                        expiresAt = System.currentTimeMillis() +
                                response.data.getLong("expires_in") * 1000
                    )
                    apiSecret = response.data.optString("api_secret", "")
                    Log.i(TAG, "设备认证成功")
                    callback(true, "认证成功")
                } else {
                    callback(false, response.message)
                }
            } catch (e: Exception) {
                Log.e(TAG, "认证失败", e)
                callback(false, e.message ?: "未知错误")
            }
        }
    }

    /**
     * 刷新JWT令牌
     */
    private fun refreshAuthToken(): Boolean {
        val token = authToken ?: return false
        try {
            val body = JSONObject().apply {
                put("refresh_token", token.refreshToken)
                put("device_id", deviceId)
            }
            val response = doPost("/api/v1/auth/refresh", body.toString(), skipAuth = true)
            if (response.isSuccess && response.data != null) {
                authToken = AuthToken(
                    accessToken = response.data.getString("access_token"),
                    refreshToken = response.data.optString("refresh_token", token.refreshToken),
                    expiresAt = System.currentTimeMillis() +
                            response.data.getLong("expires_in") * 1000
                )
                Log.i(TAG, "Token刷新成功")
                return true
            }
        } catch (e: Exception) {
            Log.e(TAG, "Token刷新失败", e)
        }
        return false
    }

    /** 确保Token有效（5分钟内过期则刷新） */
    private fun ensureValidToken() {
        val token = authToken ?: return
        val remaining = token.expiresAt - System.currentTimeMillis()
        if (remaining < 5 * 60 * 1000) {
            refreshAuthToken()
        }
    }

    /** 计算请求签名 HMAC-SHA256 */
    private fun signRequest(method: String, path: String, body: String?): String {
        if (apiSecret.isEmpty()) return ""
        val timestamp = System.currentTimeMillis().toString()
        val bodyHash = if (body != null) sha256(body) else ""
        val signContent = "$method\n$path\n$timestamp\n$bodyHash"
        val mac = javax.crypto.Mac.getInstance("HmacSHA256")
        mac.init(javax.crypto.spec.SecretKeySpec(apiSecret.toByteArray(), "HmacSHA256"))
        return mac.doFinal(signContent.toByteArray()).joinToString("") { "%02x".format(it) }
    }

    private fun sha256(input: String): String {
        val digest = MessageDigest.getInstance("SHA-256")
        return digest.digest(input.toByteArray()).joinToString("") { "%02x".format(it) }
    }

    /** 发送GET请求 */
    fun doGet(path: String): ApiResponse = executeRequest("GET", path, null)

    /** 发送POST请求 */
    fun doPost(path: String, body: String, skipAuth: Boolean = false): ApiResponse =
        executeRequest("POST", path, body, skipAuth)

    /** 执行HTTP请求（带重试） */
    private fun executeRequest(method: String, path: String, body: String?,
                                skipAuth: Boolean = false): ApiResponse {
        var lastException: Exception? = null
        for (retry in 0 until MAX_RETRIES) {
            try {
                if (!skipAuth) ensureValidToken()
                val url = URL("$baseUrl$path")
                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("Accept", "application/json")

                if (!skipAuth) {
                    authToken?.let {
                        conn.setRequestProperty("Authorization", "${it.tokenType} ${it.accessToken}")
                    }
                }
                val signature = signRequest(method, path, body)
                if (signature.isNotEmpty()) {
                    conn.setRequestProperty("X-Signature", signature)
                    conn.setRequestProperty("X-Timestamp", System.currentTimeMillis().toString())
                }
                if (body != null && method == "POST") {
                    conn.doOutput = true
                    conn.outputStream.bufferedWriter().use { it.write(body) }
                }
                val responseCode = conn.responseCode
                val responseBody = if (responseCode in 200..299) {
                    conn.inputStream.bufferedReader().readText()
                } else {
                    conn.errorStream?.bufferedReader()?.readText() ?: ""
                }
                conn.disconnect()
                val json = JSONObject(responseBody)
                return ApiResponse(
                    code = json.optInt("code", responseCode),
                    message = json.optString("msg", ""),
                    data = json.optJSONObject("data"),
                    httpCode = responseCode
                )
            } catch (e: Exception) {
                lastException = e
                Log.w(TAG, "$method $path 失败(${retry + 1}/$MAX_RETRIES): ${e.message}")
                if (retry < MAX_RETRIES - 1) Thread.sleep(1000L * (retry + 1))
            }
        }
        return ApiResponse(-1, lastException?.message ?: "请求失败", null, 0)
    }

    /** 获取课堂信息 */
    fun getClassroomInfo(classroomId: String, callback: (ApiResponse) -> Unit) {
        requestExecutor.submit { callback(doGet("/api/v1/classroom/$classroomId")) }
    }

    /** 上传课堂录像（分片上传） */
    fun uploadRecording(filePath: String, classroomId: String,
                          callback: (Boolean, String) -> Unit) {
        requestExecutor.submit {
            try {
                val file = File(filePath)
                if (!file.exists()) {
                    callback(false, "文件不存在")
                    return@submit
                }
                Log.i(TAG, "上传录像: ${file.name}, 大小=${file.length() / 1024}KB")

                if (file.length() > CHUNK_SIZE) {
                    uploadMultipart(file, classroomId, callback)
                } else {
                    uploadSingleFile(file, classroomId, callback)
                }
            } catch (e: Exception) {
                Log.e(TAG, "上传失败", e)
                callback(false, e.message ?: "上传失败")
            }
        }
    }

    /** 单文件上传 */
    private fun uploadSingleFile(file: File, classroomId: String,
                                   callback: (Boolean, String) -> Unit) {
        val boundary = "----WritechBoundary${System.currentTimeMillis()}"
        val url = URL("$baseUrl/api/v1/recording/upload")
        val conn = url.openConnection() as HttpURLConnection
        conn.requestMethod = "POST"
        conn.doOutput = true
        conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=$boundary")
        authToken?.let {
            conn.setRequestProperty("Authorization", "${it.tokenType} ${it.accessToken}")
        }

        val os = DataOutputStream(conn.outputStream)
        /* 写入classroom_id字段 */
        os.writeBytes("--$boundary\r\n")
        os.writeBytes("Content-Disposition: form-data; name=\"classroom_id\"\r\n\r\n")
        os.writeBytes("$classroomId\r\n")
        /* 写入文件数据 */
        os.writeBytes("--$boundary\r\n")
        os.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"${file.name}\"\r\n")
        os.writeBytes("Content-Type: video/mp4\r\n\r\n")
        FileInputStream(file).use { fis ->
            val buffer = ByteArray(8192)
            var bytesRead: Int
            while (fis.read(buffer).also { bytesRead = it } != -1) {
                os.write(buffer, 0, bytesRead)
            }
        }
        os.writeBytes("\r\n--$boundary--\r\n")
        os.flush()

        val responseCode = conn.responseCode
        conn.disconnect()

        if (responseCode in 200..299) {
            Log.i(TAG, "录像上传成功: ${file.name}")
            callback(true, "上传成功")
        } else {
            callback(false, "HTTP $responseCode")
        }
    }

    /** 分片上传大文件 */
    private fun uploadMultipart(file: File, classroomId: String,
                                  callback: (Boolean, String) -> Unit) {
        val fileSize = file.length()
        val totalChunks = ((fileSize + CHUNK_SIZE - 1) / CHUNK_SIZE).toInt()
        Log.i(TAG, "分片上传: ${totalChunks}片, 文件大小=${fileSize / 1024}KB")

        /* 1. 初始化分片上传 */
        val initBody = JSONObject().apply {
            put("classroom_id", classroomId)
            put("file_name", file.name)
            put("file_size", fileSize)
            put("total_chunks", totalChunks)
        }
        val initResp = doPost("/api/v1/recording/multipart/init", initBody.toString())
        if (!initResp.isSuccess) {
            callback(false, "初始化分片上传失败: ${initResp.message}")
            return
        }
        val uploadId = initResp.data?.optString("upload_id", "") ?: ""

        /* 2. 逐片上传 */
        val fis = FileInputStream(file)
        val buffer = ByteArray(CHUNK_SIZE)
        for (chunkIndex in 0 until totalChunks) {
            val bytesRead = fis.read(buffer)
            if (bytesRead <= 0) break

            Log.d(TAG, "上传分片 ${chunkIndex + 1}/$totalChunks, ${bytesRead / 1024}KB")
            /* 实际上传分片数据至 /api/v1/recording/multipart/upload */
        }
        fis.close()

        /* 3. 完成合并 */
        val completeBody = JSONObject().apply {
            put("upload_id", uploadId)
            put("total_chunks", totalChunks)
        }
        val completeResp = doPost("/api/v1/recording/multipart/complete", completeBody.toString())
        if (completeResp.isSuccess) {
            Log.i(TAG, "分片上传完成: ${file.name}")
            callback(true, "上传成功")
        } else {
            callback(false, "合并失败: ${completeResp.message}")
        }
    }

    /** 同步课堂数据（笔迹统计、互动结果等） */
    fun syncClassroomData(classroomId: String, data: JSONObject,
                            callback: (ApiResponse) -> Unit) {
        requestExecutor.submit {
            callback(doPost("/api/v1/classroom/$classroomId/sync", data.toString()))
        }
    }

    /** 设备心跳上报 */
    fun reportHeartbeat(status: JSONObject) {
        requestExecutor.submit {
            status.put("device_id", deviceId)
            status.put("timestamp", System.currentTimeMillis())
            doPost("/api/v1/device/heartbeat", status.toString())
        }
    }

    /** 关闭客户端 */
    fun shutdown() {
        requestExecutor.shutdown()
        Log.i(TAG, "API客户端已关闭")
    }
}
