package cn.learningpad.oraltrainer.sdk

import android.content.Context
import android.net.Uri
import android.os.Handler
import android.os.Looper
import org.json.JSONObject
import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

data class TrainingVideoSummary(
    val videoHash: String,
    val title: String,
    val streamUri: Uri,
    val durationMs: Long?,
    val sizeBytes: Long,
    val language: String?,
    val sentenceCount: Int,
    val status: String,
) {
    fun toTrainingMediaItem(sentences: List<SentenceBoundary> = emptyList()): TrainingMediaItem {
        return TrainingMediaItem(
            id = videoHash,
            title = title,
            uri = streamUri,
            sentences = sentences,
            customCacheKey = videoHash,
        )
    }
}

interface VideoCatalogCallback {
    fun onSuccess(videos: List<TrainingVideoSummary>)

    fun onError(error: Throwable)
}

class VideoCatalogApi internal constructor(
    context: Context,
    private val config: OralTrainerSdkConfig,
) {
    private val mainHandler = Handler(Looper.getMainLooper())
    private val executor: ExecutorService = Executors.newCachedThreadPool()

    fun fetch(callback: VideoCatalogCallback): CancellableRequest {
        val future = executor.submit {
            try {
                val videos = fetchBlocking()
                if (!Thread.currentThread().isInterrupted) {
                    mainHandler.post { callback.onSuccess(videos) }
                }
            } catch (error: Throwable) {
                if (!Thread.currentThread().isInterrupted) {
                    mainHandler.post { callback.onError(error) }
                }
            }
        }
        return CancellableRequest(future)
    }

    fun release() {
        executor.shutdownNow()
    }

    private fun fetchBlocking(): List<TrainingVideoSummary> {
        return HttpFallback.forConfig(config).execute(config.sentenceBoundaryApiBaseUrl) { effectiveBaseUrl ->
            val endpoint = URL(URL(effectiveBaseUrl), "api/v1/videos")
            val connection = endpoint.openConnection() as HttpURLConnection
            try {
                connection.requestMethod = "GET"
                connection.connectTimeout = config.connectTimeoutMs
                connection.readTimeout = config.readTimeoutMs
                connection.setRequestProperty("Accept", "application/json")
                connection.setRequestProperty("User-Agent", config.userAgent)
                val statusCode = connection.responseCode
                val stream = if (statusCode in 200..299) connection.inputStream else connection.errorStream
                val body = stream?.bufferedReader(Charsets.UTF_8)?.use { it.readText() }.orEmpty()
                if (statusCode !in 200..299) throw VideoCatalogApiException(statusCode, body)
                parseResponse(body, effectiveBaseUrl)
            } finally {
                connection.disconnect()
            }
        }
    }

    private fun parseResponse(body: String, effectiveBaseUrl: String): List<TrainingVideoSummary> {
        val videos = JSONObject(body).getJSONArray("videos")
        val effectiveScheme = Uri.parse(effectiveBaseUrl).scheme
        return buildList(videos.length()) {
            for (index in 0 until videos.length()) {
                val item = videos.getJSONObject(index)
                val status = item.getString("status")
                if (status != "ready") continue
                val resolvedStreamUrl = URL(URL(effectiveBaseUrl), item.getString("stream_url")).toString()
                val streamUri = if (effectiveScheme == "http") {
                    Uri.parse(resolvedStreamUrl).buildUpon().scheme("http").build().toString()
                } else {
                    resolvedStreamUrl
                }
                add(
                    TrainingVideoSummary(
                        videoHash = item.getString("video_hash"),
                        title = item.getString("title"),
                        streamUri = Uri.parse(streamUri),
                        durationMs = if (item.isNull("duration_ms")) null else item.getLong("duration_ms"),
                        sizeBytes = item.getLong("size_bytes"),
                        language = if (item.isNull("language")) null else item.getString("language"),
                        sentenceCount = item.getInt("sentence_count"),
                        status = status,
                    )
                )
            }
        }
    }
}

class VideoCatalogApiException(
    val statusCode: Int,
    responseBody: String,
) : IOException("Video catalog API returned HTTP $statusCode: $responseBody")
