package app.closer.data.repository

import android.content.Context
import android.content.SharedPreferences
import app.closer.data.local.SecurePreferencesFactory
import app.closer.domain.model.LocalAnswer
import app.closer.domain.repository.LocalAnswerRepository
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import org.json.JSONArray
import org.json.JSONObject

@Singleton
class SharedPreferencesLocalAnswerRepository @Inject constructor(
    @ApplicationContext context: Context
) : LocalAnswerRepository {

    private val prefs: SharedPreferences = run {
        // Remove legacy plaintext file on first migration
        context.deleteSharedPreferences("local_answers")

        SecurePreferencesFactory.encryptedSharedPreferences(context, PREFS_NAME)
    }
    private val answers = MutableStateFlow(readAnswers())

    override fun observeAnswers(): Flow<List<LocalAnswer>> = answers

    override fun observeAnswer(questionId: String): Flow<LocalAnswer?> {
        return answers.map { list -> list.firstOrNull { it.questionId == questionId } }
    }

    override suspend fun getAnswer(questionId: String): LocalAnswer? {
        return answers.value.firstOrNull { it.questionId == questionId }
    }

    override suspend fun saveAnswer(answer: LocalAnswer) {
        val existing = answers.value.firstOrNull { it.questionId == answer.questionId }
        val saved = answer.copy(
            createdAt = existing?.createdAt ?: answer.createdAt,
            updatedAt = System.currentTimeMillis(),
            isRevealed = existing?.isRevealed ?: answer.isRevealed
        )
        val updated = answers.value
            .filterNot { it.questionId == saved.questionId }
            .plus(saved)
            .sortedByDescending { it.updatedAt }
        persist(updated)
    }

    override suspend fun markRevealed(questionId: String) {
        val updated = answers.value.map { answer ->
            if (answer.questionId == questionId) {
                answer.copy(isRevealed = true, updatedAt = System.currentTimeMillis())
            } else {
                answer
            }
        }
        persist(updated)
    }

    override suspend fun deleteAnswer(questionId: String) {
        persist(answers.value.filterNot { it.questionId == questionId })
    }

    private fun readAnswers(): List<LocalAnswer> {
        val raw = prefs.getString(KEY_ANSWERS, null) ?: return emptyList()
        return runCatching {
            val array = JSONArray(raw)
            (0 until array.length()).mapNotNull { index ->
                array.optJSONObject(index)?.toLocalAnswer()
            }
        }.getOrDefault(emptyList())
    }

    private fun persist(updated: List<LocalAnswer>) {
        prefs.edit()
            .putString(KEY_ANSWERS, JSONArray(updated.map { it.toJson() }).toString())
            .apply()
        answers.value = updated
    }

    private fun JSONObject.toLocalAnswer(): LocalAnswer {
        return LocalAnswer(
            questionId = optString("questionId"),
            questionText = optString("questionText"),
            category = optString("category"),
            answerType = optString("answerType"),
            writtenText = optString("writtenText").takeIf { it.isNotBlank() },
            selectedOptionIds = optStringList("selectedOptionIds"),
            selectedOptionTexts = optStringList("selectedOptionTexts"),
            scaleValue = if (has("scaleValue") && !isNull("scaleValue")) optInt("scaleValue") else null,
            createdAt = optLong("createdAt", System.currentTimeMillis()),
            updatedAt = optLong("updatedAt", System.currentTimeMillis()),
            isRevealed = optBoolean("isRevealed", false),
            schemaVersion = optInt("schemaVersion", 2),
            isSealed = optBoolean("isSealed", false),
            answerDate = optString("answerDate", "")
        )
    }

    private fun LocalAnswer.toJson(): JSONObject {
        return JSONObject()
            .put("questionId", questionId)
            .put("questionText", questionText)
            .put("category", category)
            .put("answerType", answerType)
            .put("writtenText", writtenText)
            .put("selectedOptionIds", JSONArray(selectedOptionIds))
            .put("selectedOptionTexts", JSONArray(selectedOptionTexts))
            .put("scaleValue", scaleValue)
            .put("createdAt", createdAt)
            .put("updatedAt", updatedAt)
            .put("isRevealed", isRevealed)
            .put("schemaVersion", schemaVersion)
            .put("isSealed", isSealed)
            .put("answerDate", answerDate)
    }

    private fun JSONObject.optStringList(key: String): List<String> {
        val array = optJSONArray(key) ?: return emptyList()
        return (0 until array.length()).mapNotNull { index ->
            array.optString(index).takeIf { it.isNotBlank() }
        }
    }

    private companion object {
        const val KEY_ANSWERS = "answers"
        const val PREFS_NAME = "local_answers_secure"
    }
}
