@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.utils

import android.content.Context
import eu.gaudian.translator.R
import eu.gaudian.translator.model.Language
import eu.gaudian.translator.model.TranslationHistoryItem
import eu.gaudian.translator.model.communication.ApiManager
import eu.gaudian.translator.model.repository.ApiRepository
import eu.gaudian.translator.model.repository.LanguageRepository
import eu.gaudian.translator.model.repository.SettingsRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody

class TranslationService(private val context: Context) {

    private val settingsRepository = SettingsRepository(context)
    private val apiRepository = ApiRepository(context)
    private val apiRequestHandler = ApiRequestHandler(ApiManager(context), context)
    private val languageRepository = LanguageRepository(context)

    private val libreBaseUrl: String = "http://23.88.48.47:5001"
    private val httpClient: okhttp3.OkHttpClient by lazy { okhttp3.OkHttpClient.Builder().build() }
    @Volatile private var cachedLibreLangs: Set<String>? = null

    private fun Language.toLibreCode(): String {
        val c = this.code.lowercase()
        val r = this.region.trim()
        return if (r.isBlank()) c else "$c-$r"
    }

    private suspend fun getLibreSupportedCodes(): Set<String> = withContext(Dispatchers.IO) {
        cachedLibreLangs?.let { return@withContext it }
        try {
            val url = "$libreBaseUrl/languages"
            val req = okhttp3.Request.Builder().url(url).get().build()
            val resp = httpClient.newCall(req).execute()
            val body = resp.body.string()
            val codes = mutableSetOf<String>()
            val arr = org.json.JSONArray(body)
            for (i in 0 until arr.length()) {
                val obj = arr.getJSONObject(i)
                val code = obj.optString("code").trim()
                if (code.isNotEmpty()) codes.add(code)
            }
            cachedLibreLangs = codes
            codes
        } catch (e: Exception) {
            Log.e("TranslationService", "Failed to fetch LibreTranslate languages", e)
            emptySet()
        }
    }

    private suspend fun libreTranslate(text: String, source: String?, target: String, alternatives: Int = 1): Result<String> = withContext(Dispatchers.IO) {
        try {
            val json = org.json.JSONObject().apply {
                put("q", text)
                put("source", source?.ifBlank { "auto" } ?: "auto")
                put("target", target)
                put("format", "text")
                put("alternatives", alternatives)
                put("api_key", "")
            }
            val media = "application/json; charset=utf-8".toMediaType()
            val body = json.toString().toRequestBody(media)
            val req = okhttp3.Request.Builder()
                .url("$libreBaseUrl/translate")
                .post(body)
                .header("Content-Type", "application/json")
                .build()
            val resp = httpClient.newCall(req).execute()
            val respBody = resp.body.string()
            if (!resp.isSuccessful) return@withContext Result.failure(Exception("LibreTranslate HTTP ${resp.code}"))
            val obj = org.json.JSONObject(respBody)
            val translated = obj.optString("translatedText").ifBlank { null }
            if (translated == null) Result.failure(Exception("No translatedText in response")) else Result.success(translated)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    suspend fun simpleTranslateTo(text: String, targetLanguage: Language): Result<String> = withContext(Dispatchers.IO) {
        Log.d("simpleTranslateTo: $text, $targetLanguage")
        //TODO fix this, as it is broken currently
        val useLibre = settingsRepository.useLibreTranslate.flow.first()
//        if (useLibre) {
//            val targetCode = targetLanguage.toLibreCode()
//            val supported = getLibreSupportedCodes()
//            val supportsTarget = supported.contains(targetCode) || supported.contains(targetCode.substringBefore('-'))
//            if (supportsTarget) {
//                val sourceCodeFull = languageRepository.loadSelectedSourceLanguage().first()?.toLibreCode()
//                val targetForRequest = if (supported.contains(targetCode)) targetCode else targetCode.substringBefore('-')
//                val sourceForRequest = sourceCodeFull?.let { sc -> if (supported.contains(sc)) sc else sc.substringBefore('-') }
//                val libreResult = libreTranslate(text, sourceForRequest, targetForRequest, alternatives = 3)
//                if (libreResult.isSuccess) {
//                    return@withContext libreResult.map { translated ->
//                        PunctuationPreserver.preserveSpacing(original = text, translated = translated)
//                    }
//                }
//            }
//        }

        val sourceLangName = languageRepository.loadSelectedSourceLanguage().first()?.englishName ?: "Auto"
        val template = TextTranslationRequest(
            text = text,
            sourceLanguage = sourceLangName,
            targetLanguage = targetLanguage.englishName
        )

        apiRequestHandler.executeRequest(template).map { apiResponse ->
            PunctuationPreserver.preserveSpacing(original = text, translated = apiResponse.translatedText)
        }
    }

    suspend fun translateSentence(sentence: String): Result<TranslationHistoryItem> = withContext(Dispatchers.IO) {
        val additionalInstructions = settingsRepository.customPromptTranslation.flow.first()
        val selectedSource = languageRepository.loadSelectedSourceLanguage().first()
        val sourceLangName = selectedSource?.englishName ?: "Auto"
        val targetLang = languageRepository.loadSelectedTargetLanguage().first()
            ?: return@withContext Result.failure(Exception("Target language is not selected."))

        val useLibre = settingsRepository.useLibreTranslate.flow.first()
        if (useLibre) {
            val targetCode = targetLang.toLibreCode()
            val supported = getLibreSupportedCodes()
            val supportsTarget = supported.contains(targetCode) || supported.contains(targetCode.substringBefore('-'))
            if (supportsTarget) {
                val sourceCodeFull = selectedSource?.toLibreCode()
                val targetForRequest = if (supported.contains(targetCode)) targetCode else targetCode.substringBefore('-')
                val sourceForRequest = sourceCodeFull?.let { sc -> if (supported.contains(sc)) sc else sc.substringBefore('-') }
                val libreResult = libreTranslate(sentence, sourceForRequest, targetForRequest, alternatives = 3)
                if (libreResult.isSuccess) {
                    return@withContext libreResult.map { translated ->
                        val fixed = PunctuationPreserver.preserveSpacing(original = sentence, translated = translated)
                        TranslationHistoryItem(
                            text = fixed,
                            sourceLanguageCode = selectedSource?.nameResId,
                            targetLanguageCode = selectedSource?.nameResId,
                            playable = TextToSpeechHelper.isPlayable(context, targetLang),
                            sourceText = sentence,
                            // FIXED: Removed translatedText = fixed
                            translationSource = context.getString(R.string.label_translation_server),
                            translationModel = null
                        )
                    }
                }
            }
        }

        val template = TextTranslationRequest(
            text = sentence,
            sourceLanguage = sourceLangName,
            targetLanguage = targetLang.englishName,
            additionalInstructions = additionalInstructions
        )

        val translationModel = try {
            apiRepository.getTranslationModel().first()?.displayName
        } catch (e: Exception) {
            Log.e("TranslationService", "Failed to get translation model", e)
            null
        }

        apiRequestHandler.executeRequest(template).map { apiResponse ->
            val fixed = PunctuationPreserver.preserveSpacing(original = sentence, translated = apiResponse.translatedText)
            TranslationHistoryItem(
                text = fixed,
                sourceLanguageCode = selectedSource?.nameResId,
                targetLanguageCode = selectedSource?.nameResId,
                playable = TextToSpeechHelper.isPlayable(context, targetLang),
                sourceText = sentence,
                // FIXED: Removed translatedText = fixed
                translationSource = context.getString(R.string.label_ai_model),
                translationModel = translationModel
            )
        }
    }

    suspend fun translateVocabulary(word: String): Result<TranslationHistoryItem> = withContext(Dispatchers.IO) {
        val selectedSource = languageRepository.loadSelectedSourceLanguage().first()
        val targetLang = languageRepository.loadSelectedTargetLanguage().first()
            ?: return@withContext Result.failure(Exception("Target language is not selected."))

        val useLibre = settingsRepository.useLibreTranslate.flow.first()
        if (useLibre) {
            val targetCode = targetLang.toLibreCode()
            val supported = getLibreSupportedCodes()
            val supportsTarget = supported.contains(targetCode) || supported.contains(targetCode.substringBefore('-'))
            if (supportsTarget) {
                val sourceCodeFull = selectedSource?.toLibreCode()
                val targetForRequest = if (supported.contains(targetCode)) targetCode else targetCode.substringBefore('-')
                val sourceForRequest = sourceCodeFull?.let { sc -> if (supported.contains(sc)) sc else sc.substringBefore('-') }
                val libreResult = libreTranslate(word, sourceForRequest, targetForRequest, alternatives = 1)
                if (libreResult.isSuccess) {
                    return@withContext libreResult.map { translatedText ->
                        val fixed = PunctuationPreserver.preserveSpacing(original = word, translated = translatedText)
                        TranslationHistoryItem(
                            text = fixed,
                            sourceLanguageCode = selectedSource?.nameResId,
                            targetLanguageCode = targetLang.nameResId,
                            sourceText = word,
                            // FIXED: Removed translatedText = fixed
                            translationSource = context.getString(R.string.label_translation_server),
                            translationModel = null
                        )
                    }
                }
            }
        }

        val sourceLangName = selectedSource?.englishName ?: "Auto"
        val template = TextTranslationRequest(
            text = word,
            sourceLanguage = sourceLangName,
            targetLanguage = targetLang.englishName
        )

        val translationModel = try {
            apiRepository.getTranslationModel().first()?.displayName
        } catch (e: Exception) {
            Log.e("TranslationService", "Failed to get translation model", e)
            null
        }

        apiRequestHandler.executeRequest(template).map { response ->
            val fixed = PunctuationPreserver.preserveSpacing(original = word, translated = response.translatedText)
            TranslationHistoryItem(
                text = fixed,
                sourceLanguageCode = selectedSource?.nameResId,
                targetLanguageCode = targetLang.nameResId,
                sourceText = word,
                // FIXED: Removed translatedText = fixed
                translationSource = context.getString(R.string.label_ai_model),
                translationModel = translationModel
            )
        }
    }

    suspend fun getMultipleSynonyms(word: String, contextPhrase: String? = null): Result<List<String>> = withContext(Dispatchers.IO) {
        val targetLang = languageRepository.loadSelectedTargetLanguage().first()
            ?: return@withContext Result.failure(Exception("Target language is not selected."))

        val template = SynonymListRequest(
            word = word,
            language = targetLang.englishName,
            contextPhrase = contextPhrase
        )

        apiRequestHandler.executeRequest(template).map { response ->
            response.items
        }
    }

    suspend fun explainTranslation(sourceText: String, translatedText: String): Result<String> = withContext(Dispatchers.IO) {
        val sourceLang = languageRepository.loadSelectedSourceLanguage().first()?.englishName ?: "Auto"
        val targetLang = languageRepository.loadSelectedTargetLanguage().first()
            ?: return@withContext Result.failure(Exception("Target language is not selected."))

        val template = TranslationExplanationRequest(
            sourceText = sourceText,
            translatedText = translatedText,
            sourceLanguage = sourceLang,
            targetLanguage = targetLang.englishName
        )

        apiRequestHandler.executeRequest(template).map { it.explanation }
    }

    suspend fun rephraseWithAlternative(
        sourceText: String,
        currentTranslation: String,
        originalWord: String,
        chosenAlternative: String
    ): Result<String> = withContext(Dispatchers.IO) {
        val sourceLang = languageRepository.loadSelectedSourceLanguage().first()?.englishName ?: "Auto"
        val targetLang = languageRepository.loadSelectedTargetLanguage().first()
            ?: return@withContext Result.failure(Exception("Target language is not selected."))

        val template = RephraseRequest(
            sourceText = sourceText,
            currentTranslation = currentTranslation,
            originalWord = originalWord,
            chosenAlternative = chosenAlternative,
            sourceLanguage = sourceLang,
            targetLanguage = targetLang.englishName
        )

        apiRequestHandler.executeRequest(template)
            .map { apiResponse ->
                PunctuationPreserver.preserveSpacing(original = sourceText, translated = apiResponse.translatedText)
            }
    }
}