package com.example.livingai.data.local

import android.content.ContentUris
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.example.livingai.domain.model.*
import com.example.livingai.domain.repository.business.DataSource
import com.opencsv.CSVReader
import com.opencsv.CSVWriter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import java.io.*

class CSVDataSource(
    private val context: Context,
    private val fileName: String,
    private val dispatchers: com.example.livingai.utils.CoroutineDispatchers
) : DataSource {

    private val folderName = "LivingAI"

    private val mutex = Mutex()

    private var cachedUri: Uri? = null

    private suspend fun getCsvUri(): Uri = withContext(dispatchers.io) {
        cachedUri?.let { return@withContext it }

        val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            queryOrCreateCsvQ()
        } else {
            legacyGetOrCreateFile()
        }

        cachedUri = uri
        return@withContext uri
    }

    private fun queryOrCreateCsvQ(): Uri {
        val collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

        val projection = arrayOf(
            MediaStore.Files.FileColumns._ID,
            MediaStore.Files.FileColumns.DISPLAY_NAME,
            MediaStore.Files.FileColumns.RELATIVE_PATH
        )

        val cursor = context.contentResolver.query(
            collection,
            projection,
            "${MediaStore.Files.FileColumns.DISPLAY_NAME}=?",
            arrayOf(fileName),
            null
        )

        cursor?.use {
            val idCol = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
            val pathCol = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.RELATIVE_PATH)

            while (it.moveToNext()) {
                val relPath = it.getString(pathCol) ?: ""
                if (relPath.contains(folderName)) {
                    val id = it.getLong(idCol)
                    return ContentUris.withAppendedId(collection, id)
                }
            }
        }

        // Create file if not found
        val values = ContentValues().apply {
            put(MediaStore.Files.FileColumns.DISPLAY_NAME, fileName)
            put(MediaStore.Files.FileColumns.MIME_TYPE, "text/csv")
            put(
                MediaStore.Files.FileColumns.RELATIVE_PATH,
                "${Environment.DIRECTORY_DOCUMENTS}/$folderName/"
            )
        }

        return context.contentResolver.insert(collection, values)!!.also { uri ->
            writeHeader(uri)
        }
    }

    private fun legacyGetOrCreateFile(): Uri {
        val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
        val sub = File(dir, folderName)
        if (!sub.exists()) sub.mkdirs()

        val file = File(sub, fileName)
        if (!file.exists()) {
            file.createNewFile()
            writeHeaderLegacy(file)
        }
        return Uri.fromFile(file)
    }

    private suspend fun readAllLines(): List<Array<String>> = mutex.withLock {
        val uri = getCsvUri()
        return withContext(dispatchers.io) {
            try {
                context.contentResolver.openInputStream(uri)?.use { input ->
                    val reader = CSVReader(InputStreamReader(input))
                    val lines = reader.readAll()
                    reader.close()
                    if (lines.isNotEmpty() && lines[0].contentEquals(HEADER)) lines.drop(1)
                    else lines
                } ?: emptyList()
            } catch (e: Exception) {
                e.printStackTrace()
                emptyList()
            }
        }
    }

    private suspend fun writeAllLines(lines: List<Array<String>>) = mutex.withLock {
        val uri = getCsvUri()
        withContext(dispatchers.io) {
            try {
                context.contentResolver.openOutputStream(uri, "wt")?.use { out ->
                    val writer = CSVWriter(OutputStreamWriter(out))
                    writer.writeNext(HEADER)
                    writer.writeAll(lines)
                    writer.close()
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    private fun writeHeader(uri: Uri) {
        context.contentResolver.openOutputStream(uri, "wt")?.use { out ->
            CSVWriter(OutputStreamWriter(out)).use { it.writeNext(HEADER) }
        }
    }

    private fun writeHeaderLegacy(file: File) {
        CSVWriter(FileWriter(file)).use { it.writeNext(HEADER) }
    }

    // --------------------------------------------------------------------------------------------
    // 3) PUBLIC API IMPLEMENTATION
    // --------------------------------------------------------------------------------------------

    override fun getAnimalProfiles(): Flow<PagingData<AnimalProfile>> =
        Pager(PagingConfig(pageSize = 20)) {
            AnimalDataPagingSource(this)
        }.flow

    suspend fun getAllAnimalProfiles(): List<AnimalProfile> {
        return readAllLines().mapNotNull(::parseAnimalProfile)
    }

    override fun getAnimalDetails(animalId: String): Flow<AnimalDetails?> = flow {
        emit(parseAnimalDetails(readAllLines().find { it.getOrNull(INDEX_ID) == animalId }))
    }.flowOn(Dispatchers.IO)

    override fun getAnimalRatings(animalId: String): Flow<AnimalRating?> = flow {
        emit(parseAnimalRating(readAllLines().find { it.getOrNull(INDEX_ID) == animalId }))
    }.flowOn(Dispatchers.IO)

    override suspend fun setAnimalProfile(p: AnimalProfile) {
        val lines = readAllLines().toMutableList()
        val i = lines.indexOfFirst { it.getOrNull(INDEX_ID) == p.animalId }

        if (i != -1) lines[i] = updateProfile(lines[i], p)
        else lines.add(createProfile(p))

        writeAllLines(lines)
    }

    override suspend fun setAnimalDetails(d: AnimalDetails) {
        val lines = readAllLines().toMutableList()
        val i = lines.indexOfFirst { it.getOrNull(INDEX_ID) == d.animalId }

        if (i != -1) lines[i] = updateDetails(lines[i], d)
        else lines.add(createDetails(d))

        writeAllLines(lines)
    }

    override suspend fun setAnimalRatings(r: AnimalRating) {
        val lines = readAllLines().toMutableList()
        val i = lines.indexOfFirst { it.getOrNull(INDEX_ID) == r.animalId }

        if (i != -1) lines[i] = updateRating(lines[i], r)
        else lines.add(createRating(r))

        writeAllLines(lines)
    }

    override suspend fun deleteAnimalProfile(animalId: String) {
        val lines = readAllLines().toMutableList()
        val i = lines.indexOfFirst { it.getOrNull(INDEX_ID) == animalId }
        if (i != -1) {
            lines.removeAt(i)
            writeAllLines(lines)
        }
    }

    // --------------------------------------------------------------------------------------------
    // 4) PARSERS + SERIALIZERS (exact same behavior you had)
    // --------------------------------------------------------------------------------------------

    private fun empty(): Array<String> = Array(TOTAL_COLUMNS) { "" }

    private fun parseAnimalProfile(row: Array<String>?): AnimalProfile? {
        row ?: return null
        val id = row.getOrNull(INDEX_ID)?.takeIf { it.isNotBlank() } ?: return null

        return AnimalProfile(
            animalId = id,
            name = row[INDEX_NAME],
            species = row[INDEX_SPECIES],
            breed = row[INDEX_BREED],
            sex = row[INDEX_SEX],
            weight = row[INDEX_WEIGHT].toIntOrNull() ?: 0,
            age = row[INDEX_AGE].toIntOrNull() ?: 0,
            imageUrls = row[INDEX_IMAGES].split(";").filter { it.isNotBlank() },
            overallRating = row[INDEX_RATING_OVERALL].toIntOrNull()
        )
    }

    private fun parseAnimalDetails(row: Array<String>?): AnimalDetails? {
        row ?: return null
        val id = row.getOrNull(INDEX_ID)?.takeIf { it.isNotBlank() } ?: return null

        return AnimalDetails(
            animalId = id,
            name = row[INDEX_NAME],
            species = row[INDEX_SPECIES],
            breed = row[INDEX_BREED],
            sex = row[INDEX_SEX],
            weight = row[INDEX_WEIGHT].toIntOrNull() ?: 0,
            age = row[INDEX_AGE].toIntOrNull() ?: 0,
            milkYield = row[INDEX_MILK].toIntOrNull() ?: 0,
            calvingNumber = row[INDEX_CALVING].toIntOrNull() ?: 0,
            reproductiveStatus = row[INDEX_REPRO],
            description = row[INDEX_DESC],
            images = row[INDEX_IMAGES].split(";").filter { it.isNotBlank() },
            video = row[INDEX_VIDEO]
        )
    }

    private fun parseAnimalRating(row: Array<String>?): AnimalRating? {
        row ?: return null
        val id = row.getOrNull(INDEX_ID)?.takeIf { it.isNotBlank() } ?: return null

        return AnimalRating(
            animalId = id,
            overallRating = row[INDEX_RATING_OVERALL].toIntOrNull() ?: 0,
            healthRating = row[INDEX_RATING_HEALTH].toIntOrNull() ?: 0,
            breedRating = row[INDEX_RATING_BREED].toIntOrNull() ?: 0,
            stature = row[INDEX_RATING_STATURE].toIntOrNull() ?: 0,
            chestWidth = row[INDEX_RATING_CHEST].toIntOrNull() ?: 0,
            bodyDepth = row[INDEX_RATING_BODY_DEPTH].toIntOrNull() ?: 0,
            angularity = row[INDEX_RATING_ANGULARITY].toIntOrNull() ?: 0,
            rumpAngle = row[INDEX_RATING_RUMP_ANGLE].toIntOrNull() ?: 0,
            rumpWidth = row[INDEX_RATING_RUMP_WIDTH].toIntOrNull() ?: 0,
            rearLegSet = row[INDEX_RATING_REAR_LEG_SET].toIntOrNull() ?: 0,
            rearLegRearView = row[INDEX_RATING_REAR_LEG_REAR].toIntOrNull() ?: 0,
            footAngle = row[INDEX_RATING_FOOT_ANGLE].toIntOrNull() ?: 0,
            foreUdderAttachment = row[INDEX_RATING_FORE_UDDER].toIntOrNull() ?: 0,
            rearUdderHeight = row[INDEX_RATING_REAR_UDDER_HEIGHT].toIntOrNull() ?: 0,
            centralLigament = row[INDEX_RATING_CENTRAL_LIG].toIntOrNull() ?: 0,
            udderDepth = row[INDEX_RATING_UDDER_DEPTH].toIntOrNull() ?: 0,
            frontTeatPosition = row[INDEX_RATING_FRONT_TEAT].toIntOrNull() ?: 0,
            teatLength = row[INDEX_RATING_TEAT_LEN].toIntOrNull() ?: 0,
            rearTeatPosition = row[INDEX_RATING_REAR_TEAT].toIntOrNull() ?: 0,
            locomotion = row[INDEX_RATING_LOCOMOTION].toIntOrNull() ?: 0,
            bodyConditionScore = row[INDEX_RATING_BCS].toIntOrNull() ?: 0,
            hockDevelopment = row[INDEX_RATING_HOCK].toIntOrNull() ?: 0,
            boneStructure = row[INDEX_RATING_BONE].toIntOrNull() ?: 0,
            rearUdderWidth = row[INDEX_RATING_REAR_UDDER_WIDTH].toIntOrNull() ?: 0,
            teatThickness = row[INDEX_RATING_TEAT_THICKNESS].toIntOrNull() ?: 0,
            muscularity = row[INDEX_RATING_MUSCULARITY].toIntOrNull() ?: 0,
            bodyConditionComments = row[INDEX_RATING_BODY_COND_COMMENTS]
        )
    }

    private fun updateProfile(row: Array<String>, p: AnimalProfile): Array<String> {
        row[INDEX_ID] = p.animalId
        row[INDEX_NAME] = p.name
        row[INDEX_SPECIES] = p.species
        row[INDEX_BREED] = p.breed
        row[INDEX_SEX] = p.sex
        row[INDEX_WEIGHT] = p.weight.toString()
        row[INDEX_AGE] = p.age.toString()
        row[INDEX_IMAGES] = p.imageUrls.joinToString(";")
        return row
    }

    private fun updateDetails(row: Array<String>, d: AnimalDetails): Array<String> {
        row[INDEX_ID] = d.animalId
        row[INDEX_NAME] = d.name
        row[INDEX_SPECIES] = d.species
        row[INDEX_BREED] = d.breed
        row[INDEX_SEX] = d.sex
        row[INDEX_WEIGHT] = d.weight.toString()
        row[INDEX_AGE] = d.age.toString()
        row[INDEX_MILK] = d.milkYield.toString()
        row[INDEX_CALVING] = d.calvingNumber.toString()
        row[INDEX_REPRO] = d.reproductiveStatus
        row[INDEX_DESC] = d.description
        row[INDEX_IMAGES] = d.images.joinToString(";")
        row[INDEX_VIDEO] = d.video
        return row
    }

    private fun updateRating(row: Array<String>, r: AnimalRating): Array<String> {
        row[INDEX_ID] = r.animalId
        row[INDEX_RATING_OVERALL] = r.overallRating.toString()
        row[INDEX_RATING_HEALTH] = r.healthRating.toString()
        row[INDEX_RATING_BREED] = r.breedRating.toString()
        row[INDEX_RATING_STATURE] = r.stature.toString()
        row[INDEX_RATING_CHEST] = r.chestWidth.toString()
        row[INDEX_RATING_BODY_DEPTH] = r.bodyDepth.toString()
        row[INDEX_RATING_ANGULARITY] = r.angularity.toString()
        row[INDEX_RATING_RUMP_ANGLE] = r.rumpAngle.toString()
        row[INDEX_RATING_RUMP_WIDTH] = r.rumpWidth.toString()
        row[INDEX_RATING_REAR_LEG_SET] = r.rearLegSet.toString()
        row[INDEX_RATING_REAR_LEG_REAR] = r.rearLegRearView.toString()
        row[INDEX_RATING_FOOT_ANGLE] = r.footAngle.toString()
        row[INDEX_RATING_FORE_UDDER] = r.foreUdderAttachment.toString()
        row[INDEX_RATING_REAR_UDDER_HEIGHT] = r.rearUdderHeight.toString()
        row[INDEX_RATING_CENTRAL_LIG] = r.centralLigament.toString()
        row[INDEX_RATING_UDDER_DEPTH] = r.udderDepth.toString()
        row[INDEX_RATING_FRONT_TEAT] = r.frontTeatPosition.toString()
        row[INDEX_RATING_TEAT_LEN] = r.teatLength.toString()
        row[INDEX_RATING_REAR_TEAT] = r.rearTeatPosition.toString()
        row[INDEX_RATING_LOCOMOTION] = r.locomotion.toString()
        row[INDEX_RATING_BCS] = r.bodyConditionScore.toString()
        row[INDEX_RATING_HOCK] = r.hockDevelopment.toString()
        row[INDEX_RATING_BONE] = r.boneStructure.toString()
        row[INDEX_RATING_REAR_UDDER_WIDTH] = r.rearUdderWidth.toString()
        row[INDEX_RATING_TEAT_THICKNESS] = r.teatThickness.toString()
        row[INDEX_RATING_MUSCULARITY] = r.muscularity.toString()
        row[INDEX_RATING_BODY_COND_COMMENTS] = r.bodyConditionComments
        return row
    }

    private fun createProfile(p: AnimalProfile) = updateProfile(empty(), p)
    private fun createDetails(d: AnimalDetails) = updateDetails(empty(), d)
    private fun createRating(r: AnimalRating) = updateRating(empty(), r)

    companion object {
        // Same columns as before
        const val INDEX_ID = 0
        const val INDEX_NAME = 1
        const val INDEX_SPECIES = 2
        const val INDEX_BREED = 3
        const val INDEX_SEX = 4
        const val INDEX_WEIGHT = 5
        const val INDEX_AGE = 6
        const val INDEX_MILK = 7
        const val INDEX_CALVING = 8
        const val INDEX_REPRO = 9
        const val INDEX_DESC = 10
        const val INDEX_IMAGES = 11
        const val INDEX_VIDEO = 12
        const val INDEX_RATING_OVERALL = 13
        const val INDEX_RATING_HEALTH = 14
        const val INDEX_RATING_BREED = 15
        const val INDEX_RATING_STATURE = 16
        const val INDEX_RATING_CHEST = 17
        const val INDEX_RATING_BODY_DEPTH = 18
        const val INDEX_RATING_ANGULARITY = 19
        const val INDEX_RATING_RUMP_ANGLE = 20
        const val INDEX_RATING_RUMP_WIDTH = 21
        const val INDEX_RATING_REAR_LEG_SET = 22
        const val INDEX_RATING_REAR_LEG_REAR = 23
        const val INDEX_RATING_FOOT_ANGLE = 24
        const val INDEX_RATING_FORE_UDDER = 25
        const val INDEX_RATING_REAR_UDDER_HEIGHT = 26
        const val INDEX_RATING_CENTRAL_LIG = 27
        const val INDEX_RATING_UDDER_DEPTH = 28
        const val INDEX_RATING_FRONT_TEAT = 29
        const val INDEX_RATING_TEAT_LEN = 30
        const val INDEX_RATING_REAR_TEAT = 31
        const val INDEX_RATING_LOCOMOTION = 32
        const val INDEX_RATING_BCS = 33
        const val INDEX_RATING_HOCK = 34
        const val INDEX_RATING_BONE = 35
        const val INDEX_RATING_REAR_UDDER_WIDTH = 36
        const val INDEX_RATING_TEAT_THICKNESS = 37
        const val INDEX_RATING_MUSCULARITY = 38
        const val INDEX_RATING_BODY_COND_COMMENTS = 39

        const val TOTAL_COLUMNS = 40

        val HEADER = arrayOf(
            "ID", "Name", "Species", "Breed", "Sex", "Weight", "Age", "MilkYield",
            "CalvingNum", "ReproStatus", "Description", "Images", "Video",
            "OverallRating", "HealthRating", "BreedRating", "Stature", "ChestWidth",
            "BodyDepth", "Angularity", "RumpAngle", "RumpWidth", "RearLegSet",
            "RearLegRearView", "FootAngle", "ForeUdderAttachment", "RearUdderHeight",
            "CentralLigament", "UdderDepth", "FrontTeatPosition", "TeatLength",
            "RearTeatPosition", "Locomotion", "BodyConditionScore", "HockDevelopment",
            "BoneStructure", "RearUdderWidth", "TeatThickness", "Muscularity",
            "BodyConditionComments"
        )
    }
}
