package com.framework.mvvm.model.repository

import android.content.SharedPreferences
import android.util.Log
import androidx.lifecycle.LiveData
import com.framework.app.api.Api
import com.framework.app.base.IRepository
import com.framework.mvvm.model.data.*
import com.framework.mvvm.model.db.AdmDatabase
import com.framework.mvvm.model.db.entity.*
import com.framework.mvvm.model.vo.AdmRequest
import com.framework.mvvm.model.vo.Building
import com.framework.mvvm.model.vo.UploadBuilding

class AdmRepository(
    private val api: Api,
    private val db: AdmDatabase,
    private val sp: SharedPreferences
) : IRepository {

    fun getProjects(request: AdmRequest): LiveData<Data<List<ProjectEntity>>> {
        return dataSource(
            fromDb = { db.projectDao().getProjects() },
            fromNt = { request { api.getFrame(request) } },
            isFromNt = { true },
            onSuccess = { frame ->
                val projects = frame.projects
                val bafs = frame.buildingsAndFloors
                try {
                    db.projectDao().insProject(projects)
                    db.objectDao().insObjects(bafs)
                } catch (e: Exception) {
                    Log.d("IRepository:${AdmRepository::class.simpleName}", e.message!!)
                }
            },
            onFailure = {
                println("onFailure: ${it.msg}")
            }
        )
    }

    fun getProjects(): LiveData<List<ProjectEntity>> {
        return db.projectDao().getProjects()
    }

    suspend fun insProject(projects: List<ProjectEntity>) {
        return db.projectDao().insProject(projects)
    }

    suspend fun insProblem(problem: ProblemArchEntity) {
        return db.problemArchDao().insProblem(problem)
    }

    suspend fun getProblem(): List<ProblemArchEntity> {
        return db.problemArchDao().getProblems()
    }

    suspend fun getProblemById(id: String): ProblemArchEntity {
        return db.problemArchDao().getProblemById(id)
    }

    suspend fun insFile(file: FileEntity) {
        return db.fileDao().insFile(file)
    }

    suspend fun getRefFile(refId: String): List<FileEntity> {
        return db.fileDao().getFileByRefId(refId)
    }

    suspend fun insQrcode(qrcode: QrCodeEntity) {
        return db.qrCodeDao().insQrCode(qrcode)
    }

    suspend fun getQrcode(objId: String): List<QrCodeEntity> {
        return db.qrCodeDao().getQrCodeByObjId(objId)
    }

    suspend fun getInfosConfigByObjType(type: String): List<InfosConfigEntity> {
        return db.infosConfigDao().getConfigByObjType(type)
    }

    suspend fun insObject(`object`: ObjectEntity) {
        return db.objectDao().insObject(`object`)
    }

    suspend fun getObject(objId: String): ObjectEntity? {
        return db.objectDao().getObjectById(objId)
    }

    suspend fun getSpaceByArchStateFloorId(id: String, archState: String): List<SpaceEntity> {
        return db.spaceDao().getSpaceByArchStateFloorId(id, archState)
    }

    suspend fun getSpaceByEquipStateFloorId(id: String, equipState: String): List<SpaceEntity> {
        return db.spaceDao().getSpaceByEquipStateFloorId(id, equipState)
    }

    suspend fun getBuildByObjType(objType: String): List<ObjectEntity> {
        return db.objectDao().getObjectByObjType(objType)
    }

    suspend fun getFloorByBuildingId(buildingId: String, type: String): List<ObjectEntity> {
        return db.objectDao().getObjectByBuildingId(buildingId, type)
    }

    suspend fun getSpaceBySpaceId(id: String): SpaceEntity {
        return db.spaceDao().getSpaceBySpaceId(id)
    }

    suspend fun insSpace(space: SpaceEntity) {
        return db.spaceDao().insSpace(space)
    }

    suspend fun getEquipByFloorId(id: String): List<ObjectEntity> {
        return db.objectDao().getEquipByFloorId(id, "equipment")
    }

    suspend fun getEquipByCode(code: String): EquipmentEntity {
        return db.equipmentDao().getEquipmentByCode(code)
    }

    suspend fun getEquipment(): List<EquipmentEntity> {
        return db.equipmentDao().getEquipments()
    }

    suspend fun getSysByCode(code: String): SystemEntity {
        return db.systemDao().getSysByCode(code)
    }

    suspend fun getEqProblem(equipId: String): ProblemEquipEntity? {
        return db.problemEquipDao().getProblemByEquipId(equipId)
    }

    suspend fun getRelConfigByCodeFrom(code: String): List<RelConfigEntity> {
        return db.relConfigDao().getConfigByCodeFrom(code)
    }

    suspend fun getRelConfigByCodeTo(code: String): List<RelConfigEntity> {
        return db.relConfigDao().getConfigByCodeTo(code)
    }

    suspend fun getPipeConfigByCodeFrom(code: String): List<PipeConfigEntity> {
        return db.pipeConfigDao().getConfigByCodeFrom(code)
    }

    suspend fun getPipeConfigByCodeTo(code: String): List<PipeConfigEntity> {
        return db.pipeConfigDao().getConfigByCodeTo(code)
    }

    suspend fun getRelByCode(graphCode: String, relCode: String): List<RelationEntity> {
        return db.relationDao().getRelationByCode(graphCode, relCode)
    }

    suspend fun getRelById(id: String): RelationEntity {
        return db.relationDao().getRelationById(id)
    }

    suspend fun getRelByCodeFrom(
        graphCode: String,
        relCode: String,
        equipId: String
    ): List<RelationEntity> {
        return db.relationDao().getRelationByCodeFrom(graphCode, relCode, equipId)
    }

    suspend fun getRelByCodeTo(
        graphCode: String,
        relCode: String,
        equipId: String
    ): List<RelationEntity> {
        return db.relationDao().getRelationByCodeTo(graphCode, relCode, equipId)
    }

    suspend fun getPipeByFloorId(floorId: String): List<PipeEntity> {
        return db.pipeDao().getPipeByFloorId(floorId)
    }

    suspend fun getPipeById(id: String): PipeEntity {
        return db.pipeDao().getPipeById(id)
    }

    suspend fun getPipeByIdFrom(
        equipId: String,
        pipeType: String,
        pipeSubType: String
    ): List<PipeEntity> {
        return db.pipeDao().getPipeByIdFrom(equipId, pipeType, pipeSubType)
    }

    suspend fun getPipeByIdTo(
        equipId: String,
        pipeType: String,
        pipeSubType: String
    ): List<PipeEntity> {
        return db.pipeDao().getPipeByIdTo(equipId, pipeType, pipeSubType)
    }

    suspend fun getObjectByCode(code: String): List<ObjectEntity> {
        return db.objectDao().getObjectByCode(code)
    }

    suspend fun getInfosConfigByEquipCode(code: String): List<InfosConfigEntity> {
        return db.infosConfigDao().getConfigByEquipCode("equipment", code)
    }

    suspend fun insRelation(relation: RelationEntity) {
        return db.relationDao().insRelation(relation)
    }

    suspend fun insPipe(relation: PipeEntity) {
        return db.pipeDao().insPipe(relation)
    }

    suspend fun insProblemEquipEntity(problem: ProblemEquipEntity) {
        return db.problemEquipDao().insProblem(problem)
    }


    /**
     * 请求框架数据
     */
    suspend fun requestFrame(request: AdmRequest) {
        request(
            from = { api.getFrame(request) },
            to = { frame ->
                println("frame: $frame")
                val projects = frame.projects
                val objects = frame.buildingsAndFloors
                try {
                    db.projectDao().insProject(projects)
                    db.objectDao().insObjects(objects)
                } catch (e: Exception) {
                    Log.d("IRepository:${AdmRepository::class.simpleName}", e.message!!)
                }
            },
            error = {
                Log.d("requestFrame error: ", it)
            }
        )
    }

    /**
     * 请求字典数据
     */
    suspend fun requestDict(request: AdmRequest) {
        request(
            from = { api.getDict(request) },
            to = { dict ->
                val pipeConfig = dict.pipeConfig
                val infosConfig = dict.infosConfig
                val config = dict.config
                val m2dTypes = dict.m2dTypes
                val relConfig = dict.relConfig
                val system = dict.dict.system
                val equipment = dict.dict.equipment
                val relation = dict.dict.relation
                val major = dict.dict.major

                try {
                    db.pipeConfigDao().insConfigs(pipeConfig)
                    db.infosConfigDao().insConfigs(infosConfig)
                    db.configDao().insConfigs(config)
                    db.m2dEquipDao().insM2ds(m2dTypes)
                    db.relConfigDao().insConfigs(relConfig)
                    db.systemDao().insSystems(system)
                    db.equipmentDao().insEquipments(equipment)
                    db.relationDictDao().insRelationDicts(relation)
                    db.majorDao().insMajors(major)
                } catch (e: Exception) {
                    Log.d("requestDict", e.message!!)
                }
            },
            error = { println("requestDict error:$it") }
        )
    }

    /**
     * 请求建筑数据
     */
    suspend fun requestBuilding(request: AdmRequest, block: ((Building) -> Unit)? = null) {
        request(
            from = { api.getBuilding(request) },
            to = { building ->
                block?.invoke(building)
                val file = building.file
                val space = building.jobSpace
                val objects = building.objects
                val problemArch = building.problemArch
                val problemEquip = building.problemEquip
                val qrCode = building.qrCode
                val relations = building.relations
                val pipes = building.pipes
                try {
                    db.fileDao().insFiles(file)
                    db.spaceDao().insSpaces(space)
                    db.objectDao().insObjects(objects)
                    db.problemArchDao().insProblems(problemArch)
                    db.problemEquipDao().insProblems(problemEquip)
                    db.qrCodeDao().insQrCodes(qrCode)
                    db.relationDao().insRelations(relations)
                    db.pipeDao().insPipes(pipes)
                } catch (e: Exception) {
                    Log.d(
                        "AdmRepository requestBuilding",
                        e.message ?: "requestBuilding has error"
                    )
                }
            },
            error = { println("requestBuilding error: $it") }
        )
    }

    /**
     * 返回楼层
     */
    suspend fun getFloors(): List<ObjectEntity> {
        return db.objectDao().getFloors()
    }


    /**
     * 返回建筑
     */
    suspend fun getBuildings(): List<ObjectEntity> {
        return db.objectDao().getBuildings()
    }

    /**
     * 更新 ObjectEntity
     */
    suspend fun updateObject(obj: ObjectEntity) {
        db.objectDao().updateObject(obj)
    }

    /**
     * 上传建筑数据
     */
    suspend fun uploadBuilding(projectId: String, buildingId: String) {
        try {
            val pipeDao = db.pipeDao()
            val fileDao = db.fileDao()
            val spaceDao = db.spaceDao()
            val qrCodeDao = db.qrCodeDao()
            val objectDao = db.objectDao()
            val relationDao = db.relationDao()
            val problemArchDao = db.problemArchDao()
            val problemEquipDao = db.problemEquipDao()

            val pipes = pipeDao.getPipeByBuildingId(buildingId).filter { it.state == "1" }
            val files = fileDao.getFileByBuildingId(buildingId).filter { it.state == "1" }
            val spaces = spaceDao.getSpaceByBuildingId(buildingId).filter { it.state == "1" }
            val qrCodes = qrCodeDao.getQrCodeByBuildingId(buildingId).filter { it.state == "1" }
            val objects = objectDao.getObjectByBuildingId(buildingId).filter { it.state == "1" }
            val relations =
                relationDao.getRelationByBuildingId(buildingId).filter { it.state == "1" }
            val problemArchs =
                problemArchDao.getProblemByBuildingId(buildingId).filter { it.state == "1" }
            val problemEquips =
                problemEquipDao.getProblemByBuildingId(buildingId).filter { it.state == "1" }

            val deviceId = sp.getString("deviceId", "") ?: ""

            val building = Building(
                buildingId = buildingId,
                file = files,
                qrCode = qrCodes,
                objects = objects,
                jobSpace = spaces,
                relations = relations,
                problemArch = problemArchs,
                problemEquip = problemEquips,
                pipes = pipes,
            )

            val uploadBuilding = UploadBuilding(
                projectId = projectId,
                uploadFlag = deviceId,
                uploadData = building
            )

            when (val response = request { api.uploadBuilding(uploadBuilding) }) {
                is EmptyResponse -> {
                }
                is FailureResponse -> {
                    Log.e("AdmViewModel", "uploadBuilding fail:${response.msg}")
                }
                is SuccessResponse -> {
                    val result = response.data
                    Log.e("AdmViewModel", "uploadBuilding success: $result")
                    files.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        fileDao.insFile(it)
                    }
                    pipes.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        pipeDao.insPipe(it)
                    }
                    spaces.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        spaceDao.insSpace(it)
                    }
                    objects.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        objectDao.insObject(it)
                    }
                    qrCodes.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        qrCodeDao.insQrCode(it)
                    }
                    relations.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        relationDao.insRelation(it)
                    }
                    problemArchs.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        problemArchDao.insProblem(it)
                    }
                    problemEquips.forEach {
                        it.state = "2"
                        it.modifiedTime = result.timestamp
                        problemEquipDao.insProblem(it)

                    }
                }
            }
        } catch (e: Exception) {
            println("uploadBuilding Exception: $e")
        }
    }
}