package com.durian.tssparty.data.local

import com.durian.tssparty.domain.model.KeygenResult
import com.durian.tssparty.domain.model.Participant
import com.durian.tssparty.domain.model.SignResult
import com.durian.tssparty.domain.model.TssOutgoingMessage
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.withContext
import tsslib.MessageCallback
import tsslib.Tsslib
import javax.inject.Inject
import javax.inject.Singleton

/**
 * Bridge between Kotlin and Go TSS library via gomobile bindings
 */
@Singleton
class TssNativeBridge @Inject constructor(
    private val gson: Gson
) {
    private val _outgoingMessages = Channel<TssOutgoingMessage>(Channel.BUFFERED)
    val outgoingMessages: Flow<TssOutgoingMessage> = _outgoingMessages.receiveAsFlow()

    private val _progress = Channel<Pair<Int, Int>>(Channel.BUFFERED)
    val progress: Flow<Pair<Int, Int>> = _progress.receiveAsFlow()

    private val _errors = Channel<String>(Channel.BUFFERED)
    val errors: Flow<String> = _errors.receiveAsFlow()

    private val _logs = Channel<String>(Channel.BUFFERED)
    val logs: Flow<String> = _logs.receiveAsFlow()

    private val callback = object : MessageCallback {
        override fun onOutgoingMessage(messageJSON: String) {
            try {
                val message = gson.fromJson(messageJSON, TssOutgoingMessage::class.java)
                _outgoingMessages.trySend(message)
            } catch (e: Exception) {
                _errors.trySend("Failed to parse outgoing message: ${e.message}")
            }
        }

        override fun onProgress(round: Long, totalRounds: Long) {
            _progress.trySend(Pair(round.toInt(), totalRounds.toInt()))
        }

        override fun onError(errorMessage: String) {
            _errors.trySend(errorMessage)
        }

        override fun onLog(message: String) {
            _logs.trySend(message)
        }
    }

    /**
     * Start a keygen session
     */
    suspend fun startKeygen(
        sessionId: String,
        partyId: String,
        partyIndex: Int,
        thresholdT: Int,
        thresholdN: Int,
        participants: List<Participant>,
        password: String
    ): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            val participantsJson = gson.toJson(participants)
            Tsslib.startKeygen(
                sessionId,
                partyId,
                partyIndex.toLong(),
                thresholdT.toLong(),
                thresholdN.toLong(),
                participantsJson,
                password,
                callback
            )
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Start a sign session
     */
    suspend fun startSign(
        sessionId: String,
        partyId: String,
        partyIndex: Int,
        thresholdT: Int,
        thresholdN: Int,
        participants: List<Participant>,
        messageHash: String,
        shareData: String,
        password: String
    ): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            val participantsJson = gson.toJson(participants)
            Tsslib.startSign(
                sessionId,
                partyId,
                partyIndex.toLong(),
                thresholdT.toLong(),
                thresholdN.toLong(),
                participantsJson,
                messageHash,
                shareData,
                password,
                callback
            )
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Send incoming message from another party
     */
    suspend fun sendIncomingMessage(
        fromPartyIndex: Int,
        isBroadcast: Boolean,
        payload: String
    ): Result<Unit> = withContext(Dispatchers.IO) {
        try {
            Tsslib.sendIncomingMessage(fromPartyIndex.toLong(), isBroadcast, payload)
            Result.success(Unit)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Wait for keygen result
     */
    suspend fun waitForKeygenResult(password: String): Result<KeygenResult> = withContext(Dispatchers.IO) {
        try {
            val resultJson = Tsslib.waitForKeygenResult(password)
            val result = gson.fromJson(resultJson, KeygenResult::class.java)
            Result.success(result)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Wait for sign result
     */
    suspend fun waitForSignResult(): Result<SignResult> = withContext(Dispatchers.IO) {
        try {
            val resultJson = Tsslib.waitForSignResult()
            val result = gson.fromJson(resultJson, SignResult::class.java)
            Result.success(result)
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    /**
     * Cancel current session
     */
    fun cancelSession() {
        Tsslib.cancelSession()
    }
}
