@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.utils.dictionary

import android.content.Context
import eu.gaudian.translator.R
import eu.gaudian.translator.model.DictionaryEntry
import eu.gaudian.translator.model.EtymologyData
import eu.gaudian.translator.model.Language
import eu.gaudian.translator.model.communication.ApiManager
import eu.gaudian.translator.model.repository.DictionaryRepository
import eu.gaudian.translator.model.repository.SettingsRepository
import eu.gaudian.translator.utils.ApiRequestHandler
import eu.gaudian.translator.utils.DictionaryDefinitionRequest
import eu.gaudian.translator.utils.EtymologyRequest
import eu.gaudian.translator.utils.ExampleSentenceRequest
import eu.gaudian.translator.utils.Log
import eu.gaudian.translator.utils.WordOfTheDayRequest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withContext
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import kotlin.time.Clock
import kotlin.time.ExperimentalTime
import kotlin.time.Instant

class DictionaryService(context: Context) {
    private val appContext: Context = context.applicationContext
    private val settingsRepository = SettingsRepository(appContext)
    private val apiRequestHandler = ApiRequestHandler(
        ApiManager(appContext),
        context = appContext
    )
    private val dictionaryRepository = DictionaryRepository(appContext)

    /**
     * Retrieves user-selected dictionary options from settings and ensures
     * "Definition" is always included as a required part.
     *
     * Stored values may be either stable keys (from R.array.dictionary_content_keys)
     * or legacy, localized labels (from R.array.dictionary_content). Here we
     * normalize them to the current locale's labels so prompts stay coherent
     * even if the app language changes.
     */
    private suspend fun getActivatedDictionaryOptions(): String {
        val storedValues = settingsRepository.dictionarySwitches.flow.first()

        val resources = appContext.resources
        val keys = resources.getStringArray(R.array.dictionary_content_keys)
        val labels = resources.getStringArray(R.array.dictionary_content)

        val selectedLabels = mutableSetOf<String>()

        // Map each stored entry to a label in the current locale, handling both
        // the new key-based format and the legacy label-based format.
        for (value in storedValues) {
            val keyIndex = keys.indexOf(value)
            if (keyIndex >= 0 && keyIndex < labels.size) {
                // New: stored as stable key
                selectedLabels.add(labels[keyIndex])
            } else {
                // Legacy: stored as localized label
                val labelIndex = labels.indexOf(value)
                if (labelIndex >= 0) {
                    selectedLabels.add(labels[labelIndex])
                } else {
                    // Fallback: keep whatever was stored
                    selectedLabels.add(value)
                }
            }
        }

        // Always ensure the "Definition" part is included.
        val definitionKeyIndex = keys.indexOf("definition")
        if (definitionKeyIndex >= 0 && definitionKeyIndex < labels.size) {
            selectedLabels.add(labels[definitionKeyIndex])
        } else {
            // Fallback if arrays are inconsistent
            selectedLabels.add("Definition")
        }

        return selectedLabels.joinToString(", ")
    }

    /**
     * Searches for a word's full definition, including user-selected parts.
     * Uses the new unified API request template system.
     */
    @OptIn(ExperimentalTime::class)
    suspend fun searchDefinition(query: String, language: Language): Result<DictionaryEntry> = withContext(Dispatchers.IO) {
        try {
            Log.i("DictionaryService", "Searching definition for word: $query in language: ${language.englishName}")
            
            val requestedParts = getActivatedDictionaryOptions()
            Log.d("DictionaryService", "Requested dictionary parts: $requestedParts")

            val template = DictionaryDefinitionRequest(
                word = query,
                language = language.englishName,
                requestedParts = requestedParts
            )
            
            val result = apiRequestHandler.executeRequest(template)
            
            result.map { apiResponse ->
                Log.i("DictionaryService", "Successfully retrieved definition for: $query")
                DictionaryEntry(
                    id = 0, // Repository will set the ID upon insertion.
                    word = apiResponse.word,
                    definition = apiResponse.parts,
                    languageCode = language.nameResId,
                    languageName = language.englishName,
                    createdAt = Clock.System.now()
                )
            }.onFailure { exception ->
                Log.e("DictionaryService", "Failed to search definition for: $query", exception)
            }
        } catch (e: Exception) {
            Log.e("DictionaryService", "Unexpected error in searchDefinition for: $query", e)
            Result.failure(e)
        }
    }

    /**
     * Retrieves an example sentence for a word with its translation using the new unified API request system.
     */
    suspend fun getExampleSentence(word: String, wordTranslation: String, languageFirst: Language, languageSecond: Language): Result<Pair<String, String>> = withContext(Dispatchers.IO) {
        try {
            Log.i("DictionaryService", "Getting example sentence for word: $word (${languageFirst.englishName} -> ${languageSecond.englishName})")
            
            val template = ExampleSentenceRequest(
                word = word,
                wordTranslation = wordTranslation,
                languageFirst = languageFirst.englishName,
                languageSecond = languageSecond.englishName
            )
            
            val result = apiRequestHandler.executeRequest(template)
            
            result.map { apiResponse ->
                Log.i("DictionaryService", "Successfully retrieved example sentence for: $word")
                Pair(
                    apiResponse.sourceSentence.trim(),
                    apiResponse.targetSentence.trim()
                )
            }.onFailure { exception ->
                Log.e("DictionaryService", "Failed to get example sentence for: $word", exception)
            }
        } catch (e: Exception) {
            Log.e("DictionaryService", "Unexpected error in getExampleSentence for: $word", e)
            Result.failure(e)
        }
    }

    @OptIn(ExperimentalTime::class)
    suspend fun getWordOfTheDay(
        language: Language,
        forceRefresh: Boolean = false
    ): DictionaryEntry? = withContext(Dispatchers.IO) {
        try {
            val today: Instant = Clock.System.now()
            val todayLocalDate = java.time.Instant.ofEpochMilli(today.toEpochMilliseconds())
                .atZone(ZoneId.systemDefault())
                .toLocalDate()
            val todayString = todayLocalDate.format(DateTimeFormatter.ISO_LOCAL_DATE)

            if (!forceRefresh) {
                val lastWordOfTheDay = dictionaryRepository.loadWordOfTheDay().first()
                val createdAtInstant = lastWordOfTheDay?.createdAt
                val createdAtLocalDate = createdAtInstant?.let {
                    java.time.Instant.ofEpochMilli(it.toEpochMilliseconds())
                        .atZone(ZoneId.systemDefault())
                        .toLocalDate()
                }
                if (createdAtLocalDate?.format(DateTimeFormatter.ISO_LOCAL_DATE) == todayString) {
                    Log.d("DictionaryService", "Returning cached word of the day for: $todayString")
                    return@withContext lastWordOfTheDay
                }
            }

            Log.i("DictionaryService", "Generating new word of the day for: $todayString in language: ${language.englishName}")

            val topics = listOf(
                "science", "literature", "history", "technology", "nature",
                "art", "philosophy", "mathematics", "music", "architecture",
                "an obscure animal", "a forgotten tool", "a culinary term",
                "a mythological concept"
            )

            val randomTopic = topics.random()
            Log.d("DictionaryService", "Selected topic for word of the day: $randomTopic")

            val template = WordOfTheDayRequest(
                language = language.englishName,
                category = randomTopic
            )

            val result = apiRequestHandler.executeRequest(template)

            result.fold(
                onSuccess = { apiResponse ->
                    val newEntry = DictionaryEntry(
                        id = 0,
                        word = apiResponse.word,
                        definition = apiResponse.parts,
                        languageCode = language.nameResId,
                        languageName = language.englishName,
                        createdAt = today
                    )
                    dictionaryRepository.saveWordOfTheDay(newEntry)
                    Log.i("DictionaryService", "Successfully generated and saved word of the day: ${apiResponse.word}")
                    return@withContext newEntry
                },
                onFailure = { exception ->
                    Log.e("DictionaryService", "Error generating word of the day", exception)
                    return@withContext null
                }
            )
        } catch (e: Exception) {
            Log.e("DictionaryService", "Unexpected error in getWordOfTheDay", e)
            null
        }
    }

    suspend fun getEtymology(query: String, language: Language): Result<EtymologyData> = withContext(Dispatchers.IO) {
        try {
            Log.i("DictionaryService", "Getting etymology for word: $query in language: ${language.englishName}")
            
            val template = EtymologyRequest(
                word = query,
                language = language.englishName
            )
            
            val result = apiRequestHandler.executeRequest(template)
            
            result.map { apiResponse ->
                Log.i("DictionaryService", "Successfully retrieved etymology for: $query")
                EtymologyData(
                    word = apiResponse.word,
                    timeline = apiResponse.timeline,
                    relatedWords = apiResponse.relatedWords
                )
            }.onFailure { exception ->
                Log.e("DictionaryService", "Failed to get etymology for: $query", exception)
            }
        } catch (e: Exception) {
            Log.e("DictionaryService", "Unexpected error in getEtymology for: $query", e)
            Result.failure(e)
        }
    }
}