package com.ys.bdtp.adm.mvvm.vm.offline

import com.ys.bdtp.adm.mvvm.model.Data
import com.ys.bdtp.adm.mvvm.model.nt.Fail
import com.ys.bdtp.adm.mvvm.model.nt.request
import com.ys.bdtp.adm.mvvm.model.db.AdmDatabase
import com.ys.bdtp.adm.mvvm.model.db.entity.task.BuildingEntity
import com.ys.bdtp.adm.mvvm.model.db.entity.task.EquipmentEntity
import com.ys.bdtp.adm.mvvm.model.db.entity.task.FloorEntity
import com.ys.bdtp.adm.mvvm.model.db.relation.BuildingWithFloors
import com.ys.bdtp.adm.mvvm.model.db.relation.ProjectWithBuildings
import com.ys.bdtp.adm.mvvm.model.nt.Api
import com.ys.bdtp.adm.mvvm.model.toStateFlow
import com.ys.bdtp.adm.mvvm.model.vo.Config
import com.ys.bdtp.adm.mvvm.model.vo.Dict
import com.ys.bdtp.adm.mvvm.model.vo.BuildingsAndFloors
import com.ys.bdtp.adm.mvvm.model.vo.Mission
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.withContext

/**
 * Offline 数据厂库
 */
class OfflineRepository(
    private val api: Api,
    private val db: AdmDatabase,
) {

    /**
     * 获取字典数据
     */
    suspend fun getDict(
        body: Map<String, String>,
        success: suspend (Dict) -> Unit,
        failure: (Fail) -> Unit,
    ) {
        withContext(Dispatchers.IO) {
            request(
                request = { api.getDict(body) },
                success = { dict ->
                    val equ = dict.equipment
                    val inf = dict.infos
                    val maj = dict.major
                    val rel = dict.relation
                    val sys = dict.system

                    try {
                        equ.let { db.equipmentDictDao().insEquipments(it) }
                        inf.let { db.infoDictDao().insInfos(it) }
                        maj.let { db.majorDictDao().insMajors(it) }
                        rel.let { db.relationDictDao().insRelations(it) }
                        sys.let { db.systemDictDao().insSystems(it) }
                    } catch (e: Exception) {
                        failure(Fail("catch", "dict related db insert catch: ${e.message}"))
                    }
                    success(dict)
                },
                failure = { fail ->
                    failure(fail)
                }
            )
        }
    }

    /**
     * 获取配置数据
     */
    suspend fun getConfig(
        body: Map<String, String>,
        success: suspend (Config) -> Unit,
        failure: (Fail) -> Unit,
    ) {
        withContext(Dispatchers.IO) {
            request(
                request = { api.getConfig(body) },
                success = { config ->
                    val adpi = config.admDefineProblemInfos
                    val adpt = config.admDefineProblemTypes
                    val comp = config.component
                    val conf = config.config
                    val cont = config.containerConfig
                    val info = config.infosConfig
                    val pipe = config.pipeConfig
                    val relc = config.relConfig

                    try {
                        adpi.let { db.problemInfoConfigDao().insProblemInfos(it) }
                        adpt.let { db.problemTypeConfigDao().insProblemTypes(it) }
                        comp.let { db.componentConfigDao().insComponents(it) }
                        conf.let { db.configDao().insConfigs(it) }
                        cont.let { db.containerConfigDao().insContainers(it) }
                        info.let { db.infosConfigDao().insConfigs(it) }
                        pipe.let { db.pipeConfigDao().insConfigs(it) }
                        relc.let { db.relationConfigDao() }

                    } catch (e: Exception) {
                        failure(Fail("catch", "config related db insert catch: ${e.message}"))
                    }


                    success(config)
                },
                failure = { fail ->
                    failure(fail)
                }
            )
        }
    }

    /**
     * 获取框架数据
     */
    suspend fun getBuildingsAndFloors(
        body: Map<String, String>,
        success: suspend (BuildingsAndFloors) -> Unit,
        failure: (Fail) -> Unit,
    ) {
        withContext(Dispatchers.IO) {
            request(
                request = { api.getBuildingsAndFloors(body) },
                success = { frame ->
                    try {
                        val objects = frame.buildingsAndFloors
                        // 分离 building
                        objects.filter { it.objType == "building" }.map {
                            BuildingEntity(
                                id = it.id,
                                name = it.name,
                                objType = it.objType,
                                classCode = it.classCode,
                                grouping = it.grouping,
                                groupCode = it.groupCode,
                                projectId = it.projectId,
                                localId = it.localId,
                                localName = it.localName,
                                infos = it.infos,
                                valid = it.valid,
                                modifier = it.modifier,
                                modifiedTime = it.modifiedTime,
                                creator = it.creator,
                                creationTime = it.creationTime
                            )
                        }.let { buildings ->
                            db.buildingDao().insBuildings(buildings)
                        }
                        // 分离 floor
                        objects.filter {
                            it.objType == "floor"
                        }.map {
                            FloorEntity(
                                id = it.id,
                                name = it.name,
                                buildingId = it.buildingId,
                                objType = it.objType,
                                classCode = it.classCode,
                                grouping = it.grouping,
                                groupCode = it.groupCode,
                                projectId = it.projectId,
                                localId = it.localId,
                                localName = it.localName,
                                infos = it.infos,
                                valid = it.valid,
                                modifier = it.modifier,
                                modifiedTime = it.modifiedTime,
                                creator = it.creator,
                                creationTime = it.creationTime
                            )
                        }.let { floors ->
                            db.floorDao().insFloors(floors)
                        }
                    } catch (e: Exception) {
                        failure(Fail("catch", "config related db insert catch: ${e.message}"))
                    }
                    success(frame)
                },
                failure = { fail ->
                    failure(fail)
                }
            )
        }
    }

    /**
     * 获取任务数据, 以建筑为单位
     */
    suspend fun getMission(
        body: Map<String, String>,
        success: suspend (Mission) -> Unit,
        failure: (Fail) -> Unit,
    ) {
        withContext(Dispatchers.IO) {
            request(
                request = { api.getMission(body) },
                success = { mission ->
                    val bdtpTs = mission.bdtpDownloadTs
                    val buildingId = mission.buildingId
                    val pipes = mission.pipes
                    val workspaces = mission.jobSpace
                    val files = mission.file
                    val qrCodes = mission.qrCode
                    val serveAreas = mission.serveArea
                    val problems = mission.problems
                    val cads = mission.admCads
                    val objects = mission.objects

                    try {
                        pipes.let { db.pipeDao().insPipes(pipes) }
                        workspaces.let { db.workspaceDao().insSpaces(it) }
                        files.let { db.fileDao().insFiles(it) }
                        qrCodes.let { db.qrCodeDao().insQrCodes(it) }
                        serveAreas.let { db.serveAreaDao().insertAreas(it) }
                        problems.let { db.problemDao().insProblems(it) }
                        cads.let { db.cadDao().insCads(it) }
                        objects.map {
                            EquipmentEntity(
                                id = it.id,
                                name = it.name,
                                objType = it.objType,
                                classCode = it.classCode,
                                grouping = it.grouping,
                                groupCode = it.groupCode,
                                projectId = it.projectId,
                                localId = it.localId,
                                localName = it.localName,
                                infos = it.infos,
                                containerId = it.containerId,
                                buildingId = it.buildingId,
                                floorId = it.floorId,
                                bimId = it.bimId,
                                ts = it.ts,
                                valid = it.valid
                            )
                        }.let {
                            db.equipmentDao().insEquipments(it)
                        }
                    } catch (e: Exception) {
                        failure(Fail("catch", "mission related db insert catch: ${e}"))
                    }
                    success(mission)
                },
                failure = { fail ->
                    failure(fail)
                }
            )
        }
    }


    fun getBuildingWithFloors(): Flow<Data<BuildingWithFloors>> {
        return db.buildingDao().getBuildingWithFloors().toStateFlow()
    }


    fun getProjectWithBuildings(): Flow<Data<List<ProjectWithBuildings>>> {
        return db.projectDao().getProjectWithBuildings().toStateFlow()
    }

}
