package com.horrortv.app.domain.usecase

import android.util.Log
import com.horrortv.app.domain.model.VideoSource
import com.horrortv.app.domain.model.VideoType
import com.horrortv.app.domain.model.SubtitleTrack
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jsoup.Jsoup
import org.json.JSONArray
import org.json.JSONObject
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
import java.net.URLEncoder
import java.util.zip.GZIPInputStream

class VideoExtractor {

    companion object {
        private const val TAG = "VideoExtractor"
        private const val PLAYIMDB_BASE = "https://www.playimdb.com/title/"
        private const val TIMEOUT_MS = 15000
        private val ALLOWED_LANGUAGES = setOf("en", "es", "english", "spanish", "eng", "spa")
        private val IMDB_ID_PATTERN = Regex("""^tt\d{7,}${'$'}""")
    }

    suspend fun extractVideoSource(imdbId: String, cacheDir: File? = null): Result<VideoSource> = withContext(Dispatchers.IO) {
        try {
            if (!IMDB_ID_PATTERN.matches(imdbId)) {
                return@withContext Result.failure(VideoExtractionException("Invalid IMDb ID: $imdbId"))
            }
            Log.d(TAG, "Extracting video for: $imdbId")

            // Step 1: Fetch playimdb.com entry page
            val entryUrl = "$PLAYIMDB_BASE$imdbId"
            Log.d(TAG, "Step 1: Fetching entry: $entryUrl")
            val entryHtml = fetchPage(entryUrl)

            // Step 2: Find iframe URL (could be any domain)
            val entryDoc = Jsoup.parse(entryHtml, entryUrl)
            val embedUrl = entryDoc.selectFirst("iframe")?.absUrl("src")
            if (embedUrl.isNullOrEmpty()) {
                return@withContext Result.failure(VideoExtractionException("No embed iframe found"))
            }
            Log.d(TAG, "Step 2: Embed URL: $embedUrl")

            // Step 3: Fetch embed page and extract CONFIG JSON
            val embedHtml = fetchPage(embedUrl)
            val config = parsePlayerConfig(embedHtml)

            var videoUrl: String?
            var pageUrl: String

            if (config != null && config.containsKey("streamDataApiUrl")) {
                // Step 4a: Call the stream data API (vaplayer.ru approach)
                val apiUrl = buildStreamApiUrl(config, embedUrl)
                Log.d(TAG, "Step 4a: Calling stream API: $apiUrl")

                try {
                    val apiResponse = fetchJson(apiUrl, embedUrl)
                    val streamUrl = extractStreamUrlFromApi(apiResponse)
                    if (streamUrl != null) {
                        videoUrl = streamUrl
                        pageUrl = embedUrl
                        Log.d(TAG, "Got stream URL from API: $streamUrl")
                    } else {
                        Log.w(TAG, "API returned no stream URLs, falling back to HTML extraction")
                        videoUrl = null
                        pageUrl = embedUrl
                    }
                } catch (e: Exception) {
                    Log.w(TAG, "Stream API failed: ${e.message}, falling back to HTML extraction")
                    videoUrl = null
                    pageUrl = embedUrl
                }
            } else {
                // Step 4b: No CONFIG found — old approach, extract from HTML directly
                Log.d(TAG, "Step 4b: No CONFIG found, extracting from HTML")
                videoUrl = null
                pageUrl = embedUrl
            }

            // Step 5: If API didn't work, try extracting from HTML (old approach for cloudnestra-style pages)
            val finalVideoUrl = videoUrl ?: run {
                val doc = Jsoup.parse(embedHtml, embedUrl)

                // Try direct video URL extraction
                extractVideoUrl(doc)?.let { return@run it }

                // Try following deeper iframes (old cloudnestra prorcp pattern)
                val innerUrls = discoverStreamingUrls(doc, embedUrl)
                for (innerUrl in innerUrls) {
                    try {
                        Log.d(TAG, "Following inner: $innerUrl")
                        val innerHtml = fetchPage(innerUrl)
                        val innerDoc = Jsoup.parse(innerHtml, innerUrl)
                        extractVideoUrl(innerDoc)?.let { return@run it }

                        // One more level
                        val deepUrls = discoverStreamingUrls(innerDoc, innerUrl)
                        for (deepUrl in deepUrls) {
                            try {
                                val deepHtml = fetchPage(deepUrl)
                                val deepDoc = Jsoup.parse(deepHtml, deepUrl)
                                extractVideoUrl(deepDoc)?.let { return@run it }
                            } catch (_: Exception) {}
                        }
                    } catch (_: Exception) {}
                }
                null
            }

            if (finalVideoUrl == null) {
                return@withContext Result.failure(VideoExtractionException("No video URL found"))
            }

            // Step 6: Resolve video URL template if needed
            val resolvedUrl = resolveVideoUrl(finalVideoUrl, pageUrl)
            Log.d(TAG, "Final video URL: $resolvedUrl")

            // Step 7: Get subtitles
            val doc = Jsoup.parse(embedHtml, embedUrl)
            val subtitles = extractSubtitles(doc, embedHtml, resolvedUrl).toMutableList()
            val openSubtitles = fetchSubtitlesFromOpenSubtitles(imdbId, cacheDir)
            for (sub in openSubtitles) {
                if (!subtitles.any { it.url == sub.url }) {
                    subtitles.add(sub)
                }
            }
            val seenUrls = mutableSetOf<String>()
            val dedupedTracks = subtitles.filter { seenUrls.add(it.url) }
            val filteredSubtitles = dedupedTracks.filter { isAllowedLanguage(it.language.lowercase(), it.label.lowercase()) }
            Log.d(TAG, "Subtitles found: ${filteredSubtitles.size}")

            val videoType = determineVideoType(resolvedUrl)
            Log.d(TAG, "Video type: $videoType")

            Result.success(
                VideoSource(
                    videoUrl = resolvedUrl,
                    videoType = videoType,
                    subtitleTracks = filteredSubtitles,
                    title = imdbId
                )
            )
        } catch (e: CancellationException) {
            throw e
        } catch (e: Exception) {
            Log.e(TAG, "Error extracting video", e)
            Result.failure(VideoExtractionException(e.message ?: "Unknown error", e))
        }
    }

    private fun parsePlayerConfig(html: String): Map<String, Any>? {
        // Find CONFIG = {...} in the HTML
        val configPattern = Regex("""const\s+CONFIG\s*=\s*(\{[^<]+?\})\s*;""")
        val match = configPattern.find(html) ?: return null

        val jsonStr = match.groupValues[1]
        return try {
            val jsonObject = JSONObject(jsonStr)
            val map = mutableMapOf<String, Any>()
            for (key in jsonObject.keys()) {
                val value = jsonObject.get(key)
                map[key] = value
            }
            Log.d(TAG, "Parsed CONFIG: streamDataApiUrl=${map["streamDataApiUrl"]}, mediaId=${map["mediaId"]}, availableSources=${map["availableSources"]}")
            map
        } catch (e: Exception) {
            Log.w(TAG, "Failed to parse CONFIG JSON: ${e.message}")
            null
        }
    }

    private fun buildStreamApiUrl(config: Map<String, Any>, embedUrl: String): String {
        val mediaId = config["mediaId"] as? String ?: ""
        val idType = config["idType"] as? String ?: "imdb"
        val mediaType = config["mediaType"] as? String ?: "movie"
        val streamDataApiUrl = config["streamDataApiUrl"] as? String ?: ""
        val sourceApiUrl = config["sourceApiUrl"] as? String ?: ""
        val availableSources = config["availableSources"]

        // Use streamDataApiUrl (absolute URL like https://streamdata.vaplayer.ru/api.php)
        val baseUrl = if (streamDataApiUrl.startsWith("http")) {
            streamDataApiUrl
        } else {
            // Relative URL — resolve against embed page
            resolveUrl(streamDataApiUrl, embedUrl)
        }

        val sb = StringBuilder(baseUrl)
        sb.append(if ('?' in baseUrl) '&' else '?')

        if (idType == "imdb") {
            sb.append("imdb=").append(URLEncoder.encode(mediaId, "UTF-8"))
        } else {
            sb.append("tmdb=").append(URLEncoder.encode(mediaId, "UTF-8"))
        }
        sb.append("&type=").append(mediaType)

        // For TV shows, add season/episode
        val season = config["season"]
        val episode = config["episode"]
        if (mediaType == "tv" && season != null && episode != null) {
            sb.append("&season=").append(season).append("&episode=").append(episode)
        }

        return sb.toString()
    }

    private fun fetchJson(url: String, referer: String): String {
        var connection: HttpURLConnection? = null
        try {
            connection = (URL(url).openConnection() as HttpURLConnection).apply {
                requestMethod = "GET"
                connectTimeout = TIMEOUT_MS
                readTimeout = TIMEOUT_MS
                setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
                setRequestProperty("Accept", "application/json, */*")
                setRequestProperty("Referer", referer)
                instanceFollowRedirects = true
            }

            val responseCode = connection.responseCode
            Log.d(TAG, "API response code: $responseCode for $url")

            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw VideoExtractionException("API HTTP error: $responseCode")
            }

            val stream = connection.inputStream.let { s ->
                if (connection.contentEncoding?.contains("gzip", ignoreCase = true) == true) {
                    GZIPInputStream(s)
                } else s
            }
            return stream.bufferedReader().use { it.readText() }
        } finally {
            connection?.disconnect()
        }
    }

    private fun extractStreamUrlFromApi(apiResponse: String): String? {
        return try {
            val json = JSONObject(apiResponse)
            val statusCode = json.opt("status_code")
            // Handle both string and int status codes
            val isSuccess = statusCode == "200" || statusCode == 200 || statusCode.toString() == "200"

            if (!isSuccess) {
                Log.w(TAG, "API returned status: $statusCode")
                return null
            }

            val data = json.optJSONObject("data") ?: return null
            val streamUrls = data.optJSONArray("stream_urls") ?: return null

            // Return the first stream URL
            for (i in 0 until streamUrls.length()) {
                val url = streamUrls.getString(i)
                if (url.contains(".m3u8") || url.contains(".mp4")) {
                    return url
                }
            }
            // If no known extension, return first URL anyway
            if (streamUrls.length() > 0) return streamUrls.getString(0)

            null
        } catch (e: Exception) {
            Log.w(TAG, "Failed to parse stream API response: ${e.message}")
            null
        }
    }

    private fun fetchPage(url: String): String {
        require(url.startsWith("https://")) { "Only HTTPS URLs are allowed: $url" }
        return doHttpFetch(url)
    }

    private fun fetchManifest(url: String): String {
        return doHttpFetch(url)
    }

    private fun fetchOpenSubtitles(url: String): String {
        return doHttpFetch(url, mapOf("X-User-Agent" to "HorrorTV/1.0"))
    }

    private fun doHttpFetch(url: String, extraHeaders: Map<String, String> = emptyMap()): String {
        var connection: HttpURLConnection? = null
        try {
            connection = (URL(url).openConnection() as HttpURLConnection).apply {
                requestMethod = "GET"
                connectTimeout = TIMEOUT_MS
                readTimeout = TIMEOUT_MS
                setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")
                setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
                setRequestProperty("Accept-Language", "en-US,en;q=0.5")
                setRequestProperty("Accept-Encoding", "gzip, deflate")
                setRequestProperty("Referer", PLAYIMDB_BASE)
                extraHeaders.forEach { (key, value) -> setRequestProperty(key, value) }
                instanceFollowRedirects = true
            }

            val responseCode = connection.responseCode
            Log.d(TAG, "Response code: $responseCode for $url")

            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw VideoExtractionException("HTTP error: $responseCode")
            }

            val inputStream = connection.inputStream.let { stream ->
                if (connection.contentEncoding?.contains("gzip", ignoreCase = true) == true) {
                    GZIPInputStream(stream)
                } else {
                    stream
                }
            }

            return inputStream.bufferedReader().use { it.readText() }
        } finally {
            connection?.disconnect()
        }
    }

    private fun discoverStreamingUrls(doc: org.jsoup.nodes.Document, baseUrl: String): List<String> {
        val urls = mutableListOf<String>()
        val seen = mutableSetOf<String>()

        // 1. Find all iframes
        for (iframe in doc.select("iframe")) {
            val src = iframe.absUrl("src").ifEmpty { resolveUrl(iframe.attr("src"), baseUrl) }
            if (src.isNotEmpty() && src.startsWith("https://") && seen.add(src)) {
                urls.add(src)
                Log.d(TAG, "Discovered iframe: $src")
            }
        }

        // 2. Find JavaScript-generated player URLs (like prorcp patterns)
        for (script in doc.select("script")) {
            val content = script.html()

            // Pattern: src: "/prorcp/..." or src: "https://..."
            val srcPatterns = listOf(
                Regex("""['"]?src['"]?\s*:\s*['"]([^'"]+)['"]"""),
                Regex("""['"]?file['"]?\s*:\s*['"]([^'"]+)['"]"""),
                Regex("""['"]?source['"]?\s*:\s*['"]([^'"]+)['"]""")
            )

            for (pattern in srcPatterns) {
                pattern.findAll(content).forEach { match ->
                    val rawUrl = match.groupValues[1]
                    val resolved = resolveUrl(rawUrl, baseUrl)
                    if (resolved.startsWith("https://") && !isValidVideoUrl(resolved) && seen.add(resolved)) {
                        urls.add(resolved)
                        Log.d(TAG, "Discovered JS URL: $resolved")
                    }
                }
            }

            // Direct path patterns like /prorcp/..., /player/..., /embed/...
            val pathPatterns = listOf(
                Regex("""(/(?:prorcp|player|embed|rcp)/[A-Za-z0-9+/=_\-]+)"""),
                Regex("""['"]((/(?:prorcp|player|embed|rcp)/[^'"]+))['"]""")
            )

            for (pattern in pathPatterns) {
                pattern.findAll(content).forEach { match ->
                    val rawPath = match.groupValues[1]
                    val resolved = resolveUrl(rawPath, baseUrl)
                    if (resolved.startsWith("https://") && seen.add(resolved)) {
                        urls.add(resolved)
                        Log.d(TAG, "Discovered path: $resolved")
                    }
                }
            }
        }

        // 3. Find anchor links containing embed/stream
        for (a in doc.select("a[href]")) {
            val href = a.attr("href")
            if (href.contains("embed", ignoreCase = true) || href.contains("stream", ignoreCase = true)) {
                val resolved = resolveUrl(href, baseUrl)
                if (resolved.startsWith("https://") && seen.add(resolved)) {
                    urls.add(resolved)
                    Log.d(TAG, "Discovered anchor: $resolved")
                }
            }
        }

        return urls
    }

    private fun resolveUrl(raw: String, baseUrl: String): String {
        if (raw.startsWith("https://") || raw.startsWith("http://")) return raw
        if (raw.startsWith("//")) return "https:$raw"
        if (raw.startsWith("/")) {
            return try {
                val base = URL(baseUrl)
                "${base.protocol}://${base.host}$raw"
            } catch (_: Exception) { raw }
        }
        return try {
            URL(URL(baseUrl), raw).toString()
        } catch (_: Exception) { raw }
    }

    private fun resolveVideoUrl(videoUrl: String, pageUrl: String): String {
        if (!videoUrl.contains("{v")) return videoUrl

        return try {
            val pageHost = URL(pageUrl).host
            // Pattern: https://tmstr3.{v1}/path -> https://tmstr1.actualdomain/path
            val templatePattern = Regex("""https?://([^/]+)\.\{v\d+\}/(.+)""")
            val match = templatePattern.find(videoUrl)
            if (match != null) {
                val prefix = match.groupValues[1]
                val path = match.groupValues[2]
                "https://${prefix.substringBefore('.')}.$pageHost/$path"
            } else {
                videoUrl.replace(Regex("""\{v\d+\}"""), pageHost)
            }
        } catch (_: Exception) {
            videoUrl
        }
    }

    private fun buildResult(
        rawVideoUrl: String,
        doc: org.jsoup.nodes.Document,
        html: String,
        imdbId: String,
        cacheDir: File?,
        pageUrl: String
    ): Result<VideoSource> {
        val videoUrl = resolveVideoUrl(rawVideoUrl, pageUrl)
        Log.d(TAG, "Video URL found: $videoUrl")

        val subtitles = extractSubtitles(doc, html, videoUrl).toMutableList()
        val openSubtitles = fetchSubtitlesFromOpenSubtitles(imdbId, cacheDir)
        for (sub in openSubtitles) {
            if (!subtitles.any { it.url == sub.url }) {
                subtitles.add(sub)
            }
        }

        val seenUrls = mutableSetOf<String>()
        val dedupedTracks = subtitles.filter { seenUrls.add(it.url) }
        val filteredSubtitles = dedupedTracks.filter { isAllowedLanguage(it.language.lowercase(), it.label.lowercase()) }
        Log.d(TAG, "Subtitles found: ${filteredSubtitles.size}")

        val videoType = determineVideoType(videoUrl)
        Log.d(TAG, "Video type: $videoType")

        return Result.success(
            VideoSource(
                videoUrl = videoUrl,
                videoType = videoType,
                subtitleTracks = filteredSubtitles,
                title = imdbId
            )
        )
    }

    private fun extractVideoUrl(doc: org.jsoup.nodes.Document): String? {
        val videoElement = doc.selectFirst("video")
        if (videoElement != null) {
            val src = videoElement.absUrl("src")
            if (src.isNotEmpty()) {
                Log.d(TAG, "Found video[src]: $src")
                return src
            }

            val sourceElements = videoElement.select("source")
            for (source in sourceElements) {
                val srcAttr = source.absUrl("src")
                if (srcAttr.isNotEmpty() && isValidVideoUrl(srcAttr)) {
                    Log.d(TAG, "Found source[src]: $srcAttr")
                    return srcAttr
                }
            }
        }

        val scripts = doc.select("script")
        for (script in scripts) {
            val scriptContent = script.html()
            val urls = extractUrlsFromScript(scriptContent)
            for (url in urls) {
                if (isValidVideoUrl(url)) {
                    Log.d(TAG, "Found URL in script: $url")
                    return url
                }
            }
        }

        val allElements = doc.select("*[src], *[href], *[data-src], *[data-url]")
        for (elem in allElements) {
            val possibleUrl = elem.attr("src")
                .ifEmpty { elem.attr("href") }
                .ifEmpty { elem.attr("data-src") }
                .ifEmpty { elem.attr("data-url") }

            if (possibleUrl.isNotEmpty() && isValidVideoUrl(possibleUrl)) {
                Log.d(TAG, "Found URL in element: $possibleUrl")
                return possibleUrl
            }
        }

        val htmlText = doc.html()
        val m3u8Pattern = Regex("https?://[^\"'<>\\s]+\\.m3u8[^\"'<>\\s]*")
        val m3u8Match = m3u8Pattern.find(htmlText)
        if (m3u8Match != null) {
            Log.d(TAG, "Found .m3u8 via regex: ${m3u8Match.value}")
            return m3u8Match.value
        }

        val mp4Pattern = Regex("https?://[^\"'<>\\s]+\\.mp4[^\"'<>\\s]*")
        val mp4Match = mp4Pattern.find(htmlText)
        if (mp4Match != null) {
            Log.d(TAG, "Found .mp4 via regex: ${mp4Match.value}")
            return mp4Match.value
        }

        return null
    }

    private fun extractUrlsFromScript(scriptContent: String): List<String> {
        val urlPattern = Regex("https?://[^\"'<>\\s]+")
        return urlPattern.findAll(scriptContent)
            .map { it.value }
            .filter { isValidVideoUrl(it) }
            .toList()
    }

    private fun isValidVideoUrl(url: String): Boolean {
        val lower = url.lowercase()
        return (lower.contains(".m3u8") || lower.contains(".mp4") || lower.contains(".mkv") || lower.contains(".mpd")) &&
               (url.startsWith("http://") || url.startsWith("https://")) &&
               !lower.contains(".js") && !lower.contains(".css") && !lower.contains(".html")
    }

    private fun determineVideoType(url: String): VideoType {
        val path = try { URL(url).path.lowercase() } catch (_: Exception) { url.lowercase() }
        return when {
            path.endsWith(".m3u8") -> VideoType.HLS
            path.endsWith(".mp4") -> VideoType.MP4
            path.endsWith(".mpd") -> VideoType.DASH
            else -> VideoType.UNKNOWN
        }
    }

    private fun extractSubtitles(doc: org.jsoup.nodes.Document, htmlContent: String, videoUrl: String?): List<SubtitleTrack> {
        val tracks = mutableListOf<SubtitleTrack>()

        val trackElements = doc.select("track")
        for (track in trackElements) {
            val src = track.absUrl("src")
            val srclang = track.attr("srclang").lowercase()
            val label = track.attr("label").ifEmpty { srclang }
            val isDefault = track.hasAttr("default")

            if (src.isNotEmpty() && isAllowedLanguage(srclang, label)) {
                tracks.add(
                    SubtitleTrack(
                        url = src,
                        language = normalizeLanguage(srclang),
                        label = label,
                        isDefault = isDefault,
                        mimeType = determineMimeType(src)
                    )
                )
                Log.d(TAG, "Found track element subtitle: $srclang - $src")
            }
        }

        val subtitlePatterns = listOf(
            Regex("\"sub\"\\s*:\\s*\"([^\"]+)\""),
            Regex("\"subtitle\"\\s*:\\s*\"([^\"]+)\""),
            Regex("\"captions\"\\s*:\\s*\"([^\"]+)\""),
            Regex("\"file\"\\s*:\\s*\"([^\"]+\\.vtt[^\"]*)\""),
            Regex("\"file\"\\s*:\\s*\"([^\"]+\\.srt[^\"]*)\""),
            Regex("'sub'\\s*:\\s*'([^']+)'"),
            Regex("'subtitle'\\s*:\\s*'([^']+)'"),
            Regex("'captions'\\s*:\\s*'([^']+)'")
        )

        for (pattern in subtitlePatterns) {
            pattern.findAll(htmlContent).forEach { match ->
                val url = match.groupValues[1]
                if (url.isNotEmpty() && !tracks.any { it.url == url }) {
                    val cleanUrl = cleanSubtitleUrl(url)
                    if (cleanUrl.isNotEmpty() && isSubtitleUrl(cleanUrl)) {
                        tracks.add(
                            SubtitleTrack(
                                url = cleanUrl,
                                language = "en",
                                label = "English",
                                isDefault = false,
                                mimeType = determineMimeType(cleanUrl)
                            )
                        )
                        Log.d(TAG, "Found JS pattern subtitle: $cleanUrl")
                    }
                }
            }
        }

        val vttPattern = Regex("https?://[^\"'<>\\s]+\\.vtt[^\"'<>\\s]*")
        vttPattern.findAll(htmlContent).forEach { match ->
            val url = match.value
            if (!tracks.any { it.url == url }) {
                tracks.add(
                    SubtitleTrack(
                        url = url,
                        language = "en",
                        label = "English",
                        isDefault = false,
                        mimeType = "text/vtt"
                    )
                )
                Log.d(TAG, "Found VTT URL: $url")
            }
        }

        val srtPattern = Regex("https?://[^\"'<>\\s]+\\.srt[^\"'<>\\s]*")
        srtPattern.findAll(htmlContent).forEach { match ->
            val url = match.value
            if (!tracks.any { it.url == url }) {
                tracks.add(
                    SubtitleTrack(
                        url = url,
                        language = "en",
                        label = "English",
                        isDefault = false,
                        mimeType = "application/x-subrip"
                    )
                )
                Log.d(TAG, "Found SRT URL: $url")
            }
        }

        val jsonArrayPattern = Regex("\\[[\\s\\S]*?\"url\"[\\s\\S]*?\\]")
        jsonArrayPattern.findAll(htmlContent).forEach { match ->
            try {
                val jsonArray = match.value
                val urlPattern = Regex("\"url\"\\s*:\\s*\"([^\"]+)\"")
                val langPattern = Regex("\"lang\"\\s*:\\s*\"([^\"]+)\"")
                val labelPattern = Regex("\"label\"\\s*:\\s*\"([^\"]+)\"")

                urlPattern.findAll(jsonArray).forEach { urlMatch ->
                    val subUrl = urlMatch.groupValues[1]
                    val langMatch = langPattern.find(jsonArray)
                    val labelMatch = labelPattern.find(jsonArray)

                    val lang = langMatch?.groupValues?.get(1)?.lowercase() ?: "en"
                    val subLabel = labelMatch?.groupValues?.get(1) ?: lang

                    if (subUrl.isNotEmpty() && !tracks.any { it.url == subUrl } && isAllowedLanguage(lang, subLabel)) {
                        tracks.add(
                            SubtitleTrack(
                                url = subUrl,
                                language = normalizeLanguage(lang),
                                label = subLabel,
                                isDefault = false,
                                mimeType = determineMimeType(subUrl)
                            )
                        )
                        Log.d(TAG, "Found JSON array subtitle: $lang - $subUrl")
                    }
                }
            } catch (e: Exception) {
                Log.w(TAG, "Error parsing subtitle JSON array: ${e.message}")
            }
        }

        val jwplayerPattern = Regex("\\{[^}]*file:\"([^\"]+)\"[^}]*label:\"([^\"]+)\"[^}]*kind:\"captions\"[^}]*\\}")
        jwplayerPattern.findAll(htmlContent).forEach { match ->
            val url = match.groupValues[1]
            val label = match.groupValues[2].lowercase()

            if (url.isNotEmpty() && !tracks.any { it.url == url } && isAllowedLanguage(label, label)) {
                tracks.add(
                    SubtitleTrack(
                        url = url,
                        language = normalizeLanguage(label),
                        label = match.groupValues[2],
                        isDefault = false,
                        mimeType = determineMimeType(url)
                    )
                )
                Log.d(TAG, "Found JWPlayer subtitle: $label - $url")
            }
        }

        if (videoUrl != null && videoUrl.contains(".m3u8")) {
            val hlsSubtitles = extractHlsSubtitles(videoUrl)
            for (sub in hlsSubtitles) {
                if (!tracks.any { it.url == sub.url }) {
                    tracks.add(sub)
                    Log.d(TAG, "Found HLS manifest subtitle: ${sub.language} - ${sub.url}")
                }
            }
        }

        return tracks.filter { isAllowedLanguage(it.language.lowercase(), it.label.lowercase()) }
    }

    private fun extractHlsSubtitles(videoUrl: String): List<SubtitleTrack> {
        val tracks = mutableListOf<SubtitleTrack>()

        try {
            val manifestContent = fetchManifest(videoUrl)

            val subtitleMediaPattern = Regex(
                "#EXT-X-MEDIA:TYPE=SUBTITLES[^\\n]*LANGUAGE=\"([^\"]+)\"[^\\n]*NAME=\"([^\"]+)\"[^\\n]*(?:DEFAULT=YES)?[^\\n]*URI=\"([^\"]+)\""
            )
            subtitleMediaPattern.findAll(manifestContent).forEach { match ->
                val lang = match.groupValues[1].lowercase()
                val name = match.groupValues[2]
                val uri = match.groupValues[3]

                val fullUrl = if (uri.startsWith("http")) {
                    uri
                } else {
                    val baseUrl = videoUrl.substringBeforeLast("/") + "/"
                    baseUrl + uri
                }

                if (fullUrl.isNotEmpty() && isAllowedLanguage(lang, name)) {
                    tracks.add(
                        SubtitleTrack(
                            url = fullUrl,
                            language = normalizeLanguage(lang),
                            label = name,
                            isDefault = match.value.contains("DEFAULT=YES", ignoreCase = true),
                            mimeType = "text/vtt"
                        )
                    )
                }
            }

            val simpleSubtitlePattern = Regex("#EXT-X-MEDIA:TYPE=SUBTITLES[^\\n]*")
            simpleSubtitlePattern.findAll(manifestContent).forEach { match ->
                val line = match.value
                val langMatch = Regex("LANGUAGE=\"([^\"]+)\"").find(line)
                val nameMatch = Regex("NAME=\"([^\"]+)\"").find(line)
                val uriMatch = Regex("URI=\"([^\"]+)\"").find(line)

                val lang = langMatch?.groupValues?.get(1)?.lowercase() ?: "en"
                val name = nameMatch?.groupValues?.get(1) ?: lang
                val uri = uriMatch?.groupValues?.get(1)

                if (uri != null) {
                    val fullUrl = if (uri.startsWith("http")) {
                        uri
                    } else {
                        val baseUrl = videoUrl.substringBeforeLast("/") + "/"
                        baseUrl + uri
                    }

                    if (fullUrl.isNotEmpty() && isAllowedLanguage(lang, name) && !tracks.any { it.url == fullUrl }) {
                        tracks.add(
                            SubtitleTrack(
                                url = fullUrl,
                                language = normalizeLanguage(lang),
                                label = name,
                                isDefault = line.contains("DEFAULT=YES", true),
                                mimeType = "text/vtt"
                            )
                        )
                    }
                }
            }
        } catch (e: Exception) {
            Log.w(TAG, "Failed to extract HLS subtitles: ${e.message}")
        }

        return tracks
    }

    fun fetchSubtitlesFromOpenSubtitles(imdbId: String, cacheDir: File? = null): List<SubtitleTrack> {
        val tracks = mutableListOf<SubtitleTrack>()
        val numericId = imdbId.removePrefix("tt").trim()
        if (numericId.isEmpty()) return tracks

        val languages = listOf(
            "spa" to "es"
        )

        for ((subLangCode, normalizedLang) in languages) {
            val url = "https://rest.opensubtitles.org/search/imdbid-$numericId/sublanguageid-$subLangCode"
            try {
                val response = fetchOpenSubtitles(url)
                val jsonArray = JSONArray(response)
                for (i in 0 until jsonArray.length()) {
                    val obj = jsonArray.getJSONObject(i)
                    val downloadLink = obj.optString("SubDownloadLink", "")
                    val languageName = obj.optString("LanguageName", normalizedLang)

                    val isHearingImpaired = obj.optString("SubHearingImpaired", "0") == "1"
                    if (isHearingImpaired) continue

                    val maxPerLanguage = 10
                    val currentCount = tracks.count { it.language == normalizedLang }

                    if (downloadLink.isNotEmpty() && currentCount < maxPerLanguage) {
                        val rawFileName = obj.optString("SubFileName", "")
                        val displayName = if (rawFileName.isNotEmpty()) {
                            rawFileName.removeSuffix(".srt").removeSuffix(".sub")
                        } else {
                            if (currentCount == 0) languageName else "$languageName #${currentCount + 1}"
                        }

                        val subtitleUrl = if (downloadLink.endsWith(".gz") && cacheDir != null) {
                            val localFile = File(cacheDir, "subtitle_${normalizedLang}_${currentCount}.srt")
                            try {
                                // Delete any previous subtitle file for this slot
                                if (localFile.exists()) {
                                    localFile.delete()
                                }
                                downloadAndDecompressSubtitle(downloadLink, localFile)
                                "file://${localFile.absolutePath}"
                            } catch (e: Exception) {
                                Log.w(TAG, "Failed to decompress subtitle: ${e.message}")
                                downloadLink
                            }
                        } else {
                            downloadLink
                        }

                        tracks.add(
                            SubtitleTrack(
                                url = subtitleUrl,
                                language = normalizedLang,
                                label = displayName,
                                isDefault = currentCount == 0,
                                mimeType = "application/x-subrip"
                            )
                        )
                        Log.d(TAG, "OpenSubtitles track: $normalizedLang - $displayName - $subtitleUrl")
                    }
                }
            } catch (e: Exception) {
                Log.w(TAG, "Failed to fetch OpenSubtitles for $subLangCode: ${e.message}")
            }
        }

        return tracks
    }

    private fun downloadAndDecompressSubtitle(url: String, outputFile: File) {
        val connection = URL(url).openConnection() as HttpURLConnection
        connection.connectTimeout = TIMEOUT_MS
        connection.readTimeout = TIMEOUT_MS
        connection.setRequestProperty("User-Agent", "Mozilla/5.0")
        try {
            val inputStream = GZIPInputStream(connection.inputStream)
            val rawBytes = inputStream.readBytes()
            inputStream.close()

            // Decode: try UTF-8 first, fall back to ISO-8859-1 for Spanish/Latin subtitles
            val text = String(rawBytes, Charsets.UTF_8)
            val hasBadChars = text.contains('\uFFFD') // Unicode replacement character
            val decodedText = if (hasBadChars) {
                String(rawBytes, Charsets.ISO_8859_1)
            } else {
                text
            }

            // Write as UTF-8 so ExoPlayer can handle it correctly
            outputFile.writeText(decodedText, Charsets.UTF_8)
        } finally {
            connection.disconnect()
        }
    }

    private fun isAllowedLanguage(lang: String, label: String): Boolean {
        val normalizedLang = lang.lowercase().trim()
        val normalizedLabel = label.lowercase().trim()
        return normalizedLang == "en" || normalizedLang == "es" ||
               normalizedLang == "eng" || normalizedLang == "spa" ||
               normalizedLang.startsWith("english") || normalizedLang.startsWith("spanish") ||
               normalizedLabel.contains("english") || normalizedLabel.contains("spanish") ||
               normalizedLabel.contains("español")
    }

    private fun normalizeLanguage(lang: String): String {
        val normalized = lang.lowercase().trim()
        return when {
            normalized.contains("english") || normalized.contains("eng") -> "en"
            normalized.contains("spanish") || normalized.contains("spa") -> "es"
            normalized == "en" -> "en"
            normalized == "es" -> "es"
            else -> normalized.take(2)
        }
    }

    private fun isSubtitleUrl(url: String): Boolean {
        return url.contains(".vtt") || url.contains(".srt") ||
               url.contains("subtitle") || url.contains("caption") ||
               url.contains("sub")
    }

    private fun cleanSubtitleUrl(url: String, baseUrl: String = ""): String {
        val clean = url.trim().removeSurrounding("\"").removeSurrounding("'").trim()
        if (clean.isEmpty()) return ""
        return when {
            clean.startsWith("http://") || clean.startsWith("https://") -> clean
            clean.startsWith("/") -> {
                try {
                    val base = if (baseUrl.isEmpty()) PLAYIMDB_BASE else baseUrl
                    URL(URL(base), clean).toString()
                } catch (_: Exception) { "" }
            }
            else -> {
                try {
                    val base = if (baseUrl.isEmpty()) PLAYIMDB_BASE else baseUrl
                    URL(URL(base), clean).toString()
                } catch (_: Exception) { "" }
            }
        }
    }

    private fun determineMimeType(url: String): String {
        return when {
            url.contains(".srt", ignoreCase = true) -> "application/x-subrip"
            url.contains(".vtt", ignoreCase = true) -> "text/vtt"
            else -> "text/vtt"
        }
    }
}

class VideoExtractionException(message: String, cause: Throwable? = null) : Exception(message, cause)
