package com.omixlab.lckcontrol.p2p.channels

import android.content.Context
import android.util.Log
import com.omixlab.lckcontrol.p2p.webrtc.WebRtcPeerManager
import com.squareup.moshi.Moshi
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileInputStream
import java.nio.ByteBuffer
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class FileChannelHandler @Inject constructor(
    @ApplicationContext private val context: Context,
    private val peerManager: WebRtcPeerManager,
    private val moshi: Moshi,
) {
    companion object {
        private const val TAG = "FileChannelHandler"
    }

    private val scope = CoroutineScope(Dispatchers.IO)

    fun handleData(data: ByteArray) {
        if (data.isEmpty()) return
        val type = data[0]

        when (type) {
            FileProtocol.FILE_LIST_REQUEST -> handleFileListRequest()
            FileProtocol.FILE_REQUEST -> handleFileRequest(data)
            FileProtocol.FILE_ACK -> { /* acknowledged, continue sending */ }
        }
    }

    private fun handleFileListRequest() {
        val files = mutableListOf<Map<String, Any?>>()

        listFilesIn(context.getExternalFilesDir("clips"), files)
        listFilesIn(context.getExternalFilesDir("cortex"), files)

        val json = moshi.adapter(Any::class.java).toJson(files)
        val jsonBytes = json.toByteArray()

        val buffer = ByteBuffer.allocate(1 + jsonBytes.size)
        buffer.put(FileProtocol.FILE_LIST_RESPONSE)
        buffer.put(jsonBytes)
        peerManager.sendFileData(buffer.array())
    }

    private fun listFilesIn(dir: File?, output: MutableList<Map<String, Any?>>) {
        dir?.listFiles()?.forEach { file ->
            output.add(mapOf(
                "path" to file.absolutePath,
                "name" to file.name,
                "size" to file.length(),
                "isDirectory" to file.isDirectory,
                "modifiedAt" to file.lastModified().toString(),
            ))
        }
    }

    private fun handleFileRequest(data: ByteArray) {
        if (data.size < 17) return
        val transferId = String(data, 1, 16).trim()
        val filePath = String(data, 17, data.size - 17)

        scope.launch {
            sendFile(transferId, filePath)
        }
    }

    private fun sendFile(transferId: String, filePath: String) {
        try {
            val file = File(filePath)
            if (!file.exists()) {
                Log.e(TAG, "File not found: $filePath")
                return
            }

            // Validate path is within allowed directories
            val allowedDirs = listOf(
                context.getExternalFilesDir("clips")?.absolutePath,
                context.getExternalFilesDir("cortex")?.absolutePath,
            )
            if (allowedDirs.none { it != null && filePath.startsWith(it) }) {
                Log.e(TAG, "Path not allowed: $filePath")
                return
            }

            // Send header
            val idBytes = transferId.padEnd(16).toByteArray().take(16).toByteArray()
            val meta = """{"fileName":"${file.name}","fileSize":${file.length()}}"""
            val metaBytes = meta.toByteArray()
            val headerBuf = ByteBuffer.allocate(1 + 16 + metaBytes.size)
            headerBuf.put(FileProtocol.FILE_HEADER)
            headerBuf.put(idBytes)
            headerBuf.put(metaBytes)
            peerManager.sendFileData(headerBuf.array())

            // Send chunks
            val input = FileInputStream(file)
            val chunkBuf = ByteArray(FileProtocol.CHUNK_SIZE)
            var bytesRead: Int

            while (input.read(chunkBuf).also { bytesRead = it } > 0) {
                val msgBuf = ByteBuffer.allocate(1 + 16 + bytesRead)
                msgBuf.put(FileProtocol.FILE_CHUNK)
                msgBuf.put(idBytes)
                msgBuf.put(chunkBuf, 0, bytesRead)
                peerManager.sendFileData(msgBuf.array())
            }
            input.close()

            // Send complete
            val completeBuf = ByteBuffer.allocate(1 + 16)
            completeBuf.put(FileProtocol.FILE_COMPLETE)
            completeBuf.put(idBytes)
            peerManager.sendFileData(completeBuf.array())

            Log.d(TAG, "File transfer complete: $transferId ($filePath)")
        } catch (e: Exception) {
            Log.e(TAG, "File transfer error: $transferId", e)
        }
    }
}
