package com.omixlab.lckcontrol.data.repository

import com.omixlab.lckcontrol.data.local.dao.StreamPlanDao
import com.omixlab.lckcontrol.data.local.entity.StreamDestinationEntity
import com.omixlab.lckcontrol.data.local.entity.StreamPlanEntity
import com.omixlab.lckcontrol.data.local.entity.StreamPlanWithDestinations
import com.omixlab.lckcontrol.data.remote.CreateDestinationRequest
import com.omixlab.lckcontrol.data.remote.CreateStreamPlanRequest
import com.omixlab.lckcontrol.data.remote.LckApiService
import com.omixlab.lckcontrol.data.remote.PrepareResponse
import com.omixlab.lckcontrol.data.remote.StreamPlanResponse
import com.omixlab.lckcontrol.data.remote.UpdateStreamPlanRequest
import com.omixlab.lckcontrol.shared.StreamDestination
import com.omixlab.lckcontrol.shared.StreamPlan
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class StreamPlanRepository @Inject constructor(
    private val planDao: StreamPlanDao,
    private val apiService: LckApiService,
) {
    fun observePlans(): Flow<List<StreamPlan>> =
        planDao.observeAll().map { list -> list.map { it.toStreamPlan() } }

    fun observePlan(planId: String): Flow<StreamPlan?> =
        planDao.observeById(planId).map { it?.toStreamPlan() }

    suspend fun getPlans(): List<StreamPlan> =
        planDao.getAll().map { it.toStreamPlan() }

    suspend fun getPlan(planId: String): StreamPlan? =
        planDao.getById(planId)?.toStreamPlan()

    /** Sync plans from backend to local Room cache */
    suspend fun syncPlans() {
        val remotePlans = apiService.getStreamPlans()
        for (remote in remotePlans) {
            cacheRemotePlan(remote)
        }
    }

    /** Create plan via backend and cache locally */
    suspend fun createPlan(
        name: String,
        destinations: List<StreamDestination>,
        executionMode: String = "IN_GAME",
        gameId: String = "",
    ): StreamPlan {
        val request = CreateStreamPlanRequest(
            name = name,
            executionMode = executionMode,
            gameId = gameId.ifBlank { null },
            destinations = destinations.map { dest ->
                CreateDestinationRequest(
                    linkedAccountId = dest.linkedAccountId.ifBlank { null },
                    title = dest.title,
                    description = dest.description,
                    privacyStatus = dest.privacyStatus,
                    gameId = dest.gameId,
                    tags = dest.tags.joinToString(","),
                    rtmpUrl = dest.rtmpUrl.ifBlank { null },
                    streamKey = dest.streamKey.ifBlank { null },
                )
            },
        )
        val response = apiService.createStreamPlan(request)
        cacheRemotePlan(response)
        return planDao.getById(response.id)!!.toStreamPlan()
    }

    /** Update a DRAFT plan via backend and refresh local cache */
    suspend fun updatePlan(
        planId: String,
        name: String,
        destinations: List<StreamDestination>,
        executionMode: String,
        gameId: String,
    ): StreamPlan {
        val request = UpdateStreamPlanRequest(
            name = name,
            executionMode = executionMode,
            gameId = gameId.ifBlank { null },
            destinations = destinations.map { dest ->
                CreateDestinationRequest(
                    linkedAccountId = dest.linkedAccountId.ifBlank { null },
                    title = dest.title,
                    description = dest.description,
                    privacyStatus = dest.privacyStatus,
                    gameId = dest.gameId,
                    tags = dest.tags.joinToString(","),
                    rtmpUrl = dest.rtmpUrl.ifBlank { null },
                    streamKey = dest.streamKey.ifBlank { null },
                )
            },
        )
        val response = apiService.updateStreamPlan(planId, request)
        cacheRemotePlan(response)
        return planDao.getById(response.id)!!.toStreamPlan()
    }

    /** Prepare plan via backend — returns RTMP info */
    suspend fun preparePlan(planId: String): PrepareResponse {
        val response = apiService.prepareStreamPlan(planId)
        // Update local cache with RTMP info by destination ID
        for (dest in response.destinations) {
            planDao.updateDestinationStream(
                id = dest.id,
                rtmpUrl = dest.rtmpUrl,
                streamKey = dest.streamKey,
                broadcastId = dest.broadcastId,
                status = "READY",
            )
        }
        planDao.updateStatus(planId, "READY")
        return response
    }

    /** Start plan via backend */
    suspend fun startPlan(planId: String) {
        apiService.startStreamPlan(planId)
        planDao.updateStatus(planId, "LIVE")
    }

    /** End plan via backend */
    suspend fun endPlan(planId: String) {
        apiService.endStreamPlan(planId)
        planDao.updateStatus(planId, "ENDED")
    }

    suspend fun deletePlan(planId: String) {
        apiService.deleteStreamPlan(planId)
        planDao.deletePlan(planId)
    }

    private suspend fun cacheRemotePlan(remote: StreamPlanResponse) {
        val planEntity = StreamPlanEntity(
            planId = remote.id,
            name = remote.name,
            status = remote.status,
            executionMode = remote.executionMode ?: "IN_GAME",
            gameId = remote.gameId ?: "",
        )
        val destEntities = remote.destinations.map { d ->
            StreamDestinationEntity(
                id = d.id,
                planId = remote.id,
                service = d.serviceId,
                linkedAccountId = d.linkedAccountId,
                title = d.title,
                description = d.description,
                privacyStatus = d.privacyStatus,
                gameId = d.gameId,
                tags = d.tags,
                rtmpUrl = d.rtmpUrl,
                streamKey = d.streamKey,
                broadcastId = d.broadcastId,
                status = d.status,
            )
        }
        planDao.insertPlanWithDestinations(planEntity, destEntities)
    }

    private fun StreamPlanWithDestinations.toStreamPlan() = StreamPlan(
        planId = plan.planId,
        name = plan.name,
        status = plan.status,
        executionMode = plan.executionMode,
        gameId = plan.gameId,
        destinations = destinations.map { it.toStreamDestination() },
    )

    private fun StreamDestinationEntity.toStreamDestination() = StreamDestination(
        service = service,
        linkedAccountId = linkedAccountId,
        title = title,
        description = description,
        privacyStatus = privacyStatus,
        gameId = gameId,
        tags = tags.split(",").filter { it.isNotBlank() },
        rtmpUrl = rtmpUrl,
        streamKey = streamKey,
        broadcastId = broadcastId,
        status = status,
    )
}
