package com.dano.test1.data

import android.content.ContentValues
import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import com.dano.test1.LanguageManager
import com.dano.test1.MyApp
import org.apache.poi.ss.usermodel.Row
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import java.io.ByteArrayOutputStream
import java.io.File

/*
Aufgabe:
- Baut eine Excel-Datei (XLSX) mit allen Clients als Zeilen und einem konfigurierbaren Spalten-Layout.
- Speichert die Datei ausschließlich in den öffentlichen „Downloads“-Ordner

Datenquelle:
- Liest die Spaltenreihenfolge/Spalten-IDs über HeaderOrderRepository.loadOrderedIds().
- Holt alle Clients, Fragebogen-IDs  sowie Antworten aus der lokalen Room-Datenbank

Ausgabeformat (Sheet „Headers“):
- Zeile 1: Spalten-IDs (erste Zelle „#“ für laufende Nummer).
- Zeile 2: Englische Beschriftung/Fragetext je Spalte (ermittelt via englishQuestionForId + LanguageManager).
- Ab Zeile 3: Pro Client eine Datenzeile.
    * Für Spalten-ID „client_code“: der Client-Code.
    * Für Spalten-IDs, die einem Fragebogen entsprechen (Questionnaire-ID): „Done“/„Not Done“ (Abschlussstatus).
    * Für sonstige Spalten-IDs (Antwort-IDs): Antwortwert oder „None“, falls leer.
*/
class ExcelExportService(
    private val context: Context,
    private val headerRepo: HeaderOrderRepository
) {

    /* Baut die Excel-Datei und speichert sie ausschließlich unter "Downloads". */
    suspend fun exportHeadersForAllClients(): Uri? {
        val orderedIds = headerRepo.loadOrderedIds()
        if (orderedIds.isEmpty()) return null

        val clients = MyApp.database.clientDao().getAllClients()
        val questionnaires = MyApp.database.questionnaireDao().getAll()
        val questionnaireIdSet = questionnaires.map { it.id }.toSet()

        val wb = XSSFWorkbook()
        val sheet = wb.createSheet("Headers")
        sheet.setColumnWidth(0, 8 * 256)
        for (i in 1..orderedIds.size) sheet.setColumnWidth(i, 36 * 256)

        // Row 1: IDs
        var col = 0
        val headerRow: Row = sheet.createRow(0)
        headerRow.createCell(col++).setCellValue("#")
        orderedIds.forEach { id -> headerRow.createCell(col++).setCellValue(id) }

        // Row 2: Questions (EN)
        val questionRow: Row = sheet.createRow(1)
        var qc = 0
        questionRow.createCell(qc++).setCellValue("Question (EN)")
        for (id in orderedIds) {
            val englishQuestion = englishQuestionForId(id, questionnaireIdSet)
            questionRow.createCell(qc++).setCellValue(englishQuestion)
        }

        // Rows 3+: Values per client
        clients.forEachIndexed { rowIdx, client ->
            val row: Row = sheet.createRow(rowIdx + 2)
            var c = 0
            row.createCell(c++).setCellValue((rowIdx + 1).toDouble())

            val completedForClient = MyApp.database.completedQuestionnaireDao().getAllForClient(client.clientCode)
            val statusMap = completedForClient.associate { it.questionnaireId to it.isDone }
            val answers = MyApp.database.answerDao().getAnswersForClient(client.clientCode)
            val answerMap = answers.associate { it.questionId to it.answerValue }

            orderedIds.forEach { id ->
                val raw = when {
                    id == "client_code" -> client.clientCode
                    id in questionnaireIdSet -> if (statusMap[id] == true) "Done" else "Not Done"
                    else -> answerMap[id]?.takeIf { it.isNotBlank() } ?: "None"
                }
                val out = localizeForExportEn(id, raw)
                row.createCell(c++).setCellValue(out)
            }
        }

        val bytes = ByteArrayOutputStream().use { bos ->
            wb.write(bos); bos.toByteArray()
        }
        wb.close()

        return saveToDownloads(
            filename = "ClientHeaders.xlsx",
            mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            bytes = bytes
        )
    }

    private fun saveToDownloads(filename: String, mimeType: String, bytes: ByteArray): Uri? {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val resolver = context.contentResolver
            val values = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, filename)
                put(MediaStore.MediaColumns.MIME_TYPE, mimeType)
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
            }
            val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
            val uri = resolver.insert(collection, values)
            if (uri != null) {
                resolver.openOutputStream(uri)?.use { it.write(bytes) } ?: return null
                uri
            } else null
        } else {
            val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            if (!downloadsDir.exists()) downloadsDir.mkdirs()
            val outFile = File(downloadsDir, filename)
            outFile.writeBytes(bytes)
            MediaScannerConnection.scanFile(
                context,
                arrayOf(outFile.absolutePath),
                arrayOf(mimeType),
                null
            )
            Uri.fromFile(outFile)
        }
    }

    private suspend fun englishQuestionForId(id: String, questionnaireIdSet: Set<String>): String {
        if (id == "client_code") return "Client code"
        if (id in questionnaireIdSet && !id.contains('-')) return "Questionnaire status"

        localizeEnglishNoBrackets(id)?.let { lm ->
            if (!looksLikeId(lm, id)) return lm
        }

        val fieldPart = id.substringAfterLast('-', id)
        localizeEnglishNoBrackets(fieldPart)?.let { lm ->
            if (!looksLikeId(lm, fieldPart)) return lm
        }

        val pretty = humanizeIdToEnglish(fieldPart)
        if (pretty.isNotBlank()) return pretty
        return "Question"
    }

    private fun looksLikeId(text: String, originalId: String): Boolean {
        val normText = text.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_')
        val normId = originalId.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_')
        return normText == normId
    }

    private fun humanizeIdToEnglish(source: String): String {
        val s = source.replace(Regex("^questionnaire_\\d+_"), "").replace('_', ' ').trim()
        if (s.isBlank()) return s
        return s.split(Regex("\\s+")).joinToString(" ") { it.lowercase().replaceFirstChar { c -> c.titlecase() } }
    }

    private fun localizeEnglishNoBrackets(key: String): String? {
        val t = try { LanguageManager.getText("ENGLISH", key) } catch (_: Exception) { null }
        val m = Regex("^\\[(.*)]$").matchEntire(t?.trim() ?: "")
        val stripped = m?.groupValues?.get(1) ?: t
        if (stripped == null || stripped.isBlank() || stripped.equals(key, ignoreCase = true)) return null
        return stripped
    }

    /* Englisch für Export; belässt Done/Not Done/None. */
    private fun localizeForExportEn(id: String, raw: String): String {
        if (id == "client_code") return raw
        if (raw == "Done" || raw == "Not Done" || raw == "None") return raw
        val norm = raw.lowercase().replace(Regex("[^a-z0-9]+"), "_").trim('_')
        val candidates = buildList {
            add(raw)
            if (norm.isNotBlank() && norm != raw) add(norm)
            if (norm.isNotBlank()) { add("${id}_$norm"); add("${id}-$norm") }
        }
        for (key in candidates) localizeEnglishNoBrackets(key)?.let { return it }
        return raw
    }
}