@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.utils

import eu.gaudian.translator.model.communication.ModelType
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement

/**
 * Unified interface for all API request templates.
 * Provides a consistent way to define API requests across all services.
 */
interface ApiRequestTemplate<T> {
    /**
     * The serializer for the expected response type
     */
    val responseSerializer: KSerializer<T>

    /**
     * The model type to use for this request
     */
    val modelType: ModelType

    /**
     * The service name for logging purposes
     */
    val serviceName: String

    /**
     * Builds the prompt for the API request
     */
    fun buildPrompt(): String

    /**
     * Required fields that should be present in the JSON response
     */
    val requiredFields: List<String> get() = emptyList()
}

/**
 * Base class for API request templates with common functionality.
 */
abstract class BaseApiRequestTemplate<T> : ApiRequestTemplate<T> {

    protected val promptBuilder = PromptBuilder("")

    /**
     * Adds a detail to the prompt
     */
    protected fun addDetail(detail: String): BaseApiRequestTemplate<T> {
        promptBuilder.addDetail(detail)
        return this
    }

    /**
     * Sets the JSON response structure description
     */
    protected fun withJsonResponse(description: String): BaseApiRequestTemplate<T> {
        promptBuilder.withJsonResponse(description)
        return this
    }

    /**
     * Adds an example to the prompt
     */
    protected fun withExample(example: String): BaseApiRequestTemplate<T> {
        promptBuilder.withExample(example)
        return this
    }

    override fun buildPrompt(): String = promptBuilder.build()
}

/**
 * Template for dictionary word definition requests
 */
class DictionaryDefinitionRequest(
    word: String,
    language: String,
    requestedParts: String
) : BaseApiRequestTemplate<DictionaryApiResponse>() {

    override val responseSerializer = DictionaryApiResponse.serializer()
    override val modelType = ModelType.DICTIONARY
    override val serviceName = "DictionaryService"
    override val requiredFields = listOf("word", "parts")

    init {
        promptBuilder.basePrompt = "Act as a dictionary. Define the word '$word' in $language."
        addDetail("Provide the following parts: $requestedParts.")
        withJsonResponse("a JSON object with a 'word' key (the original word being defined) and a 'parts' array. Each item in the 'parts' array must be an object with a 'title' (e.g., 'Definition', 'Origin') and a 'content' key (the corresponding text).")
    }
}

/**
 * Template for example sentence requests
 */
class ExampleSentenceRequest(
    word: String,
    wordTranslation: String,
    languageFirst: String,
    languageSecond: String
) : BaseApiRequestTemplate<ExampleSentenceApiResponse>() {

    override val responseSerializer = ExampleSentenceApiResponse.serializer()
    override val modelType = ModelType.DICTIONARY
    override val serviceName = "DictionaryService"
    override val requiredFields = listOf("word", "sourceSentence", "targetSentence")

    init {
        promptBuilder.basePrompt = """
        Task: Create a short, concise natural example sentence in $languageFirst for the word '$word'.
        
        Rules:
        1. The 'sourceSentence' must be entirely in $languageFirst. 
        2. Do NOT use the word '$wordTranslation' in the 'sourceSentence'.
        3. The 'targetSentence' must be the $languageSecond translation of the 'sourceSentence'.
        4. In the 'targetSentence', use the word '$wordTranslation'.
    """.trimIndent()

        addDetail("Constraint: Ensure 'sourceSentence' contains ONLY $languageFirst and 'targetSentence' contains ONLY $languageSecond.")
        addDetail("Structure: { 'word': '$word', 'sourceSentence': string, 'targetSentence': string }.")
        withJsonResponse("a JSON object. Ensure no mixed-language sentences occur.")
    }
}

/**
 * Template for vocabulary translation requests
 */
class VocabularyTranslationRequest(
    words: List<String>,
    languageFirst: String,
    languageSecond: String
) : BaseApiRequestTemplate<VocabularyApiResponse>() {

    override val responseSerializer = VocabularyApiResponse.serializer()
    override val modelType = ModelType.VOCABULARY
    override val serviceName = "VocabularyService"
    override val requiredFields = listOf("flashcards")

    init {
        val wordsJson = Json { ignoreUnknownKeys = true; isLenient = true }
            .encodeToString(words.filter { it.isNotBlank() }.distinct())

        promptBuilder.basePrompt = "Translate a list of words between two languages."
        addDetail("Source language: $languageFirst. Target language: $languageSecond.")
        addDetail("Translate each source word to the target language as concise vocabulary, not sentences.")
        addDetail("Here is the JSON array of words: $wordsJson")
        withJsonResponse("a 'flashcards' array, where each item has 'front' and 'back' objects with 'language' and 'word'. The 'front' must be in $languageFirst and the 'back' in $languageSecond.")
    }
}

/**
 * Template for text translation requests
 */
class TextTranslationRequest(
    text: String,
    sourceLanguage: String,
    targetLanguage: String,
    additionalInstructions: String = ""
) : BaseApiRequestTemplate<TranslationApiResponse>() {

    override val responseSerializer = TranslationApiResponse.serializer()
    override val modelType = ModelType.TRANSLATION
    override val serviceName = "TranslationService"
    override val requiredFields = listOf("translatedText")

    init {
        promptBuilder.basePrompt = "Translate the following from $sourceLanguage to $targetLanguage: '$text'"
        addDetail("Act like a translator and translate everything and don't return anything else.")
        if (additionalInstructions.isNotBlank()) {
            addDetail("Additional instructions: $additionalInstructions")
        }
        withJsonResponse("a JSON object with a single key 'translatedText' containing only the final translation string, without any extra commentary or formatting.")
    }
}

/**
 * Template for translation explanation
 */
class TranslationExplanationRequest(
    sourceText: String,
    translatedText: String,
    sourceLanguage: String,
    targetLanguage: String
) : BaseApiRequestTemplate<ExplanationApiResponse>() {
    override val responseSerializer = ExplanationApiResponse.serializer()
    override val modelType = ModelType.TRANSLATION
    override val serviceName = "TranslationService"
    override val requiredFields = listOf("explanation")

    init {
        promptBuilder.basePrompt = "Explain briefly the translation choice when translating from $sourceLanguage to $targetLanguage."
        addDetail("Source: '$sourceText'")
        addDetail("Translation: '$translatedText'")
        addDetail("Focus on key word choices, grammar, tone, and word order.")
        withJsonResponse("a JSON object with a single key 'explanation' containing a concise explanation (1-2 sentences) in $sourceLanguage.")
    }
}

/**
 * Template for rephrasing translations
 */
class RephraseRequest(
    sourceText: String,
    currentTranslation: String,
    originalWord: String,
    chosenAlternative: String,
    sourceLanguage: String,
    targetLanguage: String
) : BaseApiRequestTemplate<TranslationApiResponse>() {
    override val responseSerializer = TranslationApiResponse.serializer()
    override val modelType = ModelType.TRANSLATION
    override val serviceName = "TranslationService"
    override val requiredFields = listOf("translatedText")

    init {
        promptBuilder.basePrompt = "You are improving a translation from $sourceLanguage to $targetLanguage."
        addDetail("Current translation: '$currentTranslation'.")
        addDetail("Replace or adapt the word/phrase '$originalWord' with '$chosenAlternative' in a natural way.")
        addDetail("Adjust grammar, word order, articles, agreement, and tone as needed so the whole sentence reads naturally in $targetLanguage.")
        addDetail("Source text: '$sourceText'")
        withJsonResponse("a JSON object with a single key 'translatedText' containing only the updated translation sentence.")
    }
}

/**
 * Template for generating a simple list of synonyms (strings)
 */
class SynonymListRequest(
    word: String,
    language: String,
    contextPhrase: String? = null
) : BaseApiRequestTemplate<StringListApiResponse>() {
    override val responseSerializer = StringListApiResponse.serializer()
    override val modelType = ModelType.TRANSLATION
    override val serviceName = "TranslationService"
    override val requiredFields = listOf("items")

    init {
        promptBuilder.basePrompt = "Act like a native speaker and provide multiple alternative forms/ways for the word '$word' in the language $language."
        if (!contextPhrase.isNullOrBlank()) {
            addDetail("The alternative forms/words must be fitting with the context: '$contextPhrase' and retain the original meaning of the context. All alternative forms must be in the same language: $language. Do not add any additional explanations")
        }
        withJsonResponse("a JSON object with a key 'items' containing an array of unique strings.")
    }
}


/**
 * Template for text correction requests
 */
class TextCorrectionRequest(
    textToCorrect: String,
    language: String,
    grammarOnly: Boolean,
    tone: String?
) : BaseApiRequestTemplate<CorrectionResponse>() {

    override val responseSerializer = CorrectionResponse.serializer()
    override val modelType = ModelType.DICTIONARY
    override val serviceName = "CorrectionService"
    override val requiredFields = listOf("correctedText", "explanation")

    init {
        val base = "Correct the grammar, spelling, and punctuation of the text, which is in $language."
        val extra = when {
            grammarOnly -> " Do not change tone, style, or word choice beyond necessary corrections."
            !tone.isNullOrBlank() -> " Also, rewrite the text in a $tone tone while preserving the original meaning."
            else -> ""
        }

        promptBuilder.basePrompt = base + extra
        withJsonResponse("containing two keys: \"correctedText\" (the fully corrected text) and \"explanation\" (a brief, one-sentence explanation of the changes).")
        withExample(textToCorrect)
    }
}

/**
 * Template for word of the day requests
 */
class WordOfTheDayRequest(
    language: String,
    category: String
) : BaseApiRequestTemplate<DictionaryApiResponse>() {

    override val responseSerializer = DictionaryApiResponse.serializer()
    override val modelType = ModelType.DICTIONARY
    override val serviceName = "DictionaryService"
    override val requiredFields = listOf("word", "parts")

    init {
        promptBuilder.basePrompt = "Provide an interesting and uncommon word and its definition in $language from the category of '$category'."
        withJsonResponse("a JSON object with a 'word' key and a 'parts' array. The 'parts' array should contain exactly one object with its 'title' as 'Definition' and 'content' as the definition of the word.")
    }
}

/**
 * Template for etymology requests
 */
class EtymologyRequest(
    word: String,
    language: String
) : BaseApiRequestTemplate<EtymologyApiResponse>() {

    override val responseSerializer = EtymologyApiResponse.serializer()
    override val modelType = ModelType.DICTIONARY
    override val serviceName = "DictionaryService"
    override val requiredFields = listOf("word", "timeline", "relatedWords")

    init {
        promptBuilder.basePrompt = "Provide a detailed etymology for the word '$word' in $language."
        addDetail("Return strictly valid JSON (RFC 8259): double-quoted keys and string values, no comments, no trailing commas, no markdown fences.")
        addDetail("Structure: { 'word': string, 'timeline': EtymologyStep[], 'relatedWords': RelatedWord[] }.")
        addDetail("EtymologyStep: { 'year': string, 'language': string, 'description': string }. Year may be an exact year like '1883' or ranges/periods like '1890s' or '20th century'.")
        addDetail("RelatedWord: { 'language': string, 'word': string }.")
        addDetail("Only include the fields above. Keep descriptions concise and factual. Sort timeline chronologically when possible.")
        withJsonResponse("a JSON object with 'word', 'timeline' (array of objects with 'year' (string), 'language' (string), 'description' (string)), and 'relatedWords' (array of objects with 'language' and 'word'). Ensure all values are properly quoted strings; no numbers for 'year'.")
    }
}

/**
 * Template for generating vocabulary items
 */
class VocabularyGenerationRequest(
    category: String,
    languageFirst: String,
    languageSecond: String,
    amount: Int,
    customPrompt: String = ""
) : BaseApiRequestTemplate<VocabularyApiResponse>() {

    override val responseSerializer = VocabularyApiResponse.serializer()
    override val modelType = ModelType.VOCABULARY
    override val serviceName = "VocabularyService"
    override val requiredFields = listOf("flashcards")

    init {
        promptBuilder.basePrompt = "Your task is to create exactly $amount unique vocabulary flashcards for the category '$category'."
        addDetail("Each flashcard must have a 'front' side in $languageFirst and a 'back' side in $languageSecond.")
        addDetail("Each side must contain exactly one word, sentence, phrase or interjection.")
        addDetail("STRICT FORMATTING RULE: Do not include any parenthetical explanations, grammatical context (e.g. 'formal', 'plural', 'masculine'), or usage notes inside the content strings.")
        addDetail("Example: Write 'your', NOT 'your (informal)'. Write 'sie', NOT 'sie (they)'.")
        addDetail("If a word is ambiguous, provide ONLY the word itself without disambiguation.")
        if (customPrompt.isNotBlank()) {
            addDetail(customPrompt)
        }
        withJsonResponse("a 'flashcards' array. Each object in the array must have a 'front' object and a 'back' object. The 'front' object must contain 'language': '$languageFirst' and 'word': '{the word}'. The 'back' object must contain 'language': '$languageSecond' and 'word': '{the translation}'.")
    }
}

/**
 * Template for generating synonyms with proximity
 */
class SynonymGenerationRequest(
    amount: Int,
    language: String,
    term: String,
    translation: String,
    translationLanguage: String,
    languageCode: String
) : BaseApiRequestTemplate<SynonymApiResponse>() {

    override val responseSerializer = SynonymApiResponse.serializer()
    override val modelType = ModelType.VOCABULARY
    override val serviceName = "VocabularyService"
    override val requiredFields = listOf("synonyms")

    init {
        promptBuilder.basePrompt = "Act as a native $language speaker. Generate $amount synonyms for the term '$term' in the $language language (ISO 639-1 code: $languageCode). The term's translation is '$translation' in $translationLanguage. For each synonym, evaluate its proximity to the original term as a percentage (100% being an identical meaning, lower percentages for broader or related terms). Respond with a JSON object containing a 'synonyms' array. Each object in the array should have a 'word' (the synonym) and a 'proximity' (the percentage as an integer). Do not return anything else."
    }
}

/**
 * Template for Grammar Classification
 */
class GrammarClassificationRequest(
    wordsToClassifyJson: String,
    possibleCategories: String
) : BaseApiRequestTemplate<VocabularyService.BatchClassificationResponse>() {
    override val responseSerializer = VocabularyService.BatchClassificationResponse.serializer()
    override val modelType = ModelType.VOCABULARY
    override val serviceName = "VocabularyService"
    override val requiredFields = listOf("results")

    init {
        promptBuilder.basePrompt = "For each word in the JSON array, determine its grammatical category."
        addDetail("Possible categories are: $possibleCategories")
        withJsonResponse("a 'results' array, each with the original 'id' and the determined 'category'.")
        addDetail("Here is the JSON array: $wordsToClassifyJson")
    }
}

/**
 * Template for Grammar Features
 */
class GrammarFeaturesRequest(
    val prompt: String
) : ApiRequestTemplate<VocabularyService.BatchGrammarResponse> {
    override val responseSerializer = VocabularyService.BatchGrammarResponse.serializer()
    override val modelType = ModelType.VOCABULARY
    override val serviceName = "VocabularyService"
    override fun buildPrompt() = prompt
    // Note: The prompt is pre-built in VocabularyService because it is highly dynamic based on config
}


/**
 * A generic request template that allows passing a raw prompt string and receiving a JsonElement.
 * Useful for complex services like ExerciseService where the prompt generation logic is external
 * and the response parsing is manual.
 */
class DynamicJsonRequest(
    private val prompt: String,
    override val modelType: ModelType,
    override val serviceName: String
) : ApiRequestTemplate<JsonElement> {
    override val responseSerializer = JsonElement.serializer()
    override fun buildPrompt(): String = prompt
}


// Response data classes
@kotlinx.serialization.Serializable
data class DictionaryApiResponse(
    val word: String,
    val parts: List<eu.gaudian.translator.model.EntryPart>
)

@kotlinx.serialization.Serializable
data class ExampleSentenceApiResponse(
    val word: String,
    val sourceSentence: String,
    val targetSentence: String
)

@kotlinx.serialization.Serializable
data class VocabularyApiResponse(
    val flashcards: List<Flashcard>
)

@kotlinx.serialization.Serializable
data class Flashcard(
    val front: CardSide,
    val back: CardSide
)

@kotlinx.serialization.Serializable
data class CardSide(
    val language: String,
    val word: String
)

@kotlinx.serialization.Serializable
data class TranslationApiResponse(
    val translatedText: String
)

@kotlinx.serialization.Serializable
data class ExplanationApiResponse(
    val explanation: String
)

@kotlinx.serialization.Serializable
data class StringListApiResponse(
    val items: List<String>
)

@kotlinx.serialization.Serializable
data class CorrectionResponse(
    val correctedText: String,
    val explanation: String
)

@kotlinx.serialization.Serializable
data class EtymologyApiResponse(
    val word: String,
    val timeline: List<eu.gaudian.translator.model.EtymologyStep> = emptyList(),
    val relatedWords: List<eu.gaudian.translator.model.RelatedWord> = emptyList()
)

@kotlinx.serialization.Serializable
data class SynonymApiResponse(
    val synonyms: List<VocabularyService.SynonymObject>
)