package com.framework.mvvm.model.repository

import android.content.SharedPreferences
import android.util.Log
import androidx.core.content.edit
import cn.hutool.core.util.IdUtil
import com.framework.app.CLIENT_ID
import com.framework.app.api.Api
import com.framework.app.base.IRepository
import com.framework.app.http.FileDownloadInfo
import com.framework.app.http.FileInfo
import com.framework.app.http.FileUploadInfo
import com.framework.app.tools.md5
import com.framework.mvvm.model.data.*
import com.framework.mvvm.model.db.AdmDatabase
import com.framework.mvvm.model.db.entity.ProjectEntity
import com.framework.mvvm.model.db.entity.UserEntity
import com.framework.mvvm.model.db.entity.dict.*
import com.framework.mvvm.model.db.entity.task.*
import com.framework.mvvm.model.vo.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.RequestBody

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

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

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

    suspend fun getProblemByAvalible(floorId: String): List<ProblemArchEntity> {
        return db.problemArchDao().getProblemByAvalible(floorId)
    }

    suspend fun getProblemByFloor(floorId: String): List<ProblemArchEntity> {
        return db.problemArchDao().getProblemByAvalible(floorId)
    }

    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 getObjectByBimId(bimId: String): ObjectEntity? {
        return db.objectDao().getObjectByBimId(bimId)
    }

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

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

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

    suspend fun getObjects(): List<ObjectEntity> {
        return db.objectDao().getObjects()
    }

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

    suspend fun getSpaceBySpaceId(id: String): JobSpaceEntity {
        return db.jobSpaceDao().getSpaceBySpaceId(id)
    }

    suspend fun insSpace(space: JobSpaceEntity) {
        return db.jobSpaceDao().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, buildingId: String): List<ObjectEntity> {
        return db.objectDao().getObjectByCode(code, buildingId)
    }

    suspend fun getObjectByLocalId(localId: String): ObjectEntity? {
        return db.objectDao().getObjectByLocalId(localId)
    }

    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 insServeArea(serveAreaEntity: ServeAreaEntity) {
        return db.serveAreaDao().insertArea(serveAreaEntity)
    }

    suspend fun getServeAreaByObjId(objId: String): ServeAreaEntity? {
        return db.serveAreaDao().getAreasByObjId(objId)
    }

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

    suspend fun getContainerConfig(container: String): List<ContainerConfigEntity> {
        return db.containerConfigDao().getWidgetsByContainer(container)
    }

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

    suspend fun getMajors(): List<MajorEntity> {
        return db.majorDao().getMajors()
    }

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

    suspend fun getCompType(code: String): List<ComponentEntity> {
        return db.componentDao().getComponentByCode(code)
    }

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


    suspend fun getRelByGraphCodeAndRelCodeAndObjFromAndObjTo(
        graphCode: String,
        relCode: String,
        objFrom: String,
        objto: String
    ): RelationEntity? {
        return db.relationDao()
            .getRelByGraphCodeAndRelCodeAndObjFromAndObjTo(graphCode, relCode, objFrom, objto)
    }

    /**
     * 返回楼层
     */
    suspend fun getFloorById(floorId: String): ObjectEntity {
        return db.objectDao().getFloorById(floorId)
    }

    suspend fun getInfoByClassCodeAndCode(
        classCode: String,
        parentClassCode: String,
        code: String
    ): InfoEntity? {
        return db.infoDao().getInfoByClassCodeAndCode(classCode, parentClassCode, code)
    }


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

    /**
     * 请求字典数据
     */
    suspend fun requestDict(request: AdmRequest) {
        request(
            from = { api.getDict(request) },
            to = { dict ->
                try {
                    dict.system?.let { db.systemDao().insSystems(it) }
                    dict.equipment?.let { db.equipmentDao().insEquipments(it) }
                    dict.relation?.let { db.relationDictDao().insRelationDicts(it) }
                    dict.major?.let { db.majorDao().insMajors(it) }
                    dict.infos?.let { db.infoDao().insInfos(it) }
                } catch (e: Exception) {
                    Log.d("requestDict", "exception: $e")
                }
            },
            error = { println("requestDict error: $it") }
        )
    }

    /**
     * 请求配置数据
     */
    suspend fun requestConfig(request: AdmRequest) {
        request(
            from = { api.getConfig(request) },
            to = { config ->
                try {
                    config.pipeConfig?.let { db.pipeConfigDao().insConfigs(it) }
                    config.infosConfig?.let { db.infosConfigDao().insConfigs(it) }
                    config.config?.let { db.configDao().insConfigs(it) }
                    config.relConfig?.let { db.relConfigDao().insConfigs(it) }
                    config.component?.let { db.componentDao().insComponents(it) }
                    config.containerConfig?.let { db.containerConfigDao().insContainers(it) }
                } catch (e: Exception) {
                    Log.d("requestDict", "exception: $e")
                }
            },
            error = { println("requestDict error: $it") }
        )
    }

    /**
     * 请求建筑数据
     */
    suspend fun requestBuilding(request: AdmRequest, block: ((Building) -> Unit)? = null) {
        request(
            from = { api.getBuilding(request) },
            to = { building ->
                block?.invoke(building)
                try {
                    building.file
                        ?.onEach { it.state = 2 }
                        ?.let { db.fileDao().insFiles(it) }
                    building.jobSpace
                        ?.onEach { it.state = 2 }
                        ?.let { db.jobSpaceDao().insSpaces(it) }
                    building.objects
                        ?.onEach { it.state = 2 }
                        ?.let { db.objectDao().insObjects(it) }
                    building.problemArch
                        ?.onEach { it.state = 2 }
                        ?.let { db.problemArchDao().insProblems(it) }
                    building.problemEquip
                        ?.onEach { it.state = 2 }
                        ?.let { db.problemEquipDao().insProblems(it) }
                    building.qrCode
                        ?.onEach { it.state = 2 }
                        ?.let { db.qrCodeDao().insQrCodes(it) }
                    building.relations
                        ?.onEach { it.state = 2 }
                        ?.let { db.relationDao().insRelations(it) }
                    building.pipes
                        ?.onEach { it.state = 2 }
                        ?.let { db.pipeDao().insPipes(it) }
                    building.areas
                        ?.onEach { it.state = 2 }
                        ?.let { db.serveAreaDao().insertAreas(it) }

                } 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()
    }

    /**
     * 返回项目下的建筑
     */
    suspend fun getBuildingByProjectId(projectId: String): List<ObjectEntity> {
        return db.objectDao().getBuildingsByProjectId(projectId)
    }

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

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

            val pipes =
                pipeDao.getPipeByBuildingId(buildingId).filter { it.state == 0 || it.state == 1 }

            val files =
                fileDao.getFileByBuildingId(buildingId).filter { it.state == 0 || it.state == 1 }

            val spaces =
                spaceDao.getSpaceByBuildingId(buildingId).filter { it.state == 0 || it.state == 1 }

            val qrCodes = qrCodeDao.getQrCodeByBuildingId(buildingId)
                .filter { it.state == 0 || it.state == 1 }

            val objects = objectDao.getObjectByBuildingId(buildingId)
                .filter { it.state == 0 || it.state == 1 }

            val relations =
                relationDao.getRelationByBuildingId(buildingId)
                    .filter { it.state == 0 || it.state == 1 }

            val problemArchs =
                problemArchDao.getProblemByBuildingId(buildingId)
                    .filter { it.state == 0 || it.state == 1 }

            val problemEquips =
                problemEquipDao.getProblemByBuildingId(buildingId)
                    .filter { it.state == 0 || it.state == 1 }

            val areas = serveAreaDao.getAreasByBuildingId(buildingId)
                .filter { it.state == 0 || it.state == 1 }

            if (pipes.isEmpty() && files.isEmpty() && spaces.isEmpty()
                && qrCodes.isEmpty() && objects.isEmpty() && relations.isEmpty()
                && problemArchs.isEmpty() && problemEquips.isEmpty() && areas.isEmpty()
            ) {
                Log.e("uploadBuilding", "nothing to upload")
                return
            }

            val clientId = sp.getString(CLIENT_ID, "") ?: ""

            val jobId = IdUtil.simpleUUID()

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

            val uploadBuilding = UploadBuilding(
                userId = userId,
                groupCode = groupCode,
                projectId = projectId,
                clientId = clientId,
                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
                        fileDao.insFile(it)
                    }
                    pipes.forEach {
                        it.state = 2
                        pipeDao.insPipe(it)
                    }
                    spaces.forEach {
                        it.state = 2
                        spaceDao.insSpace(it)
                    }
                    objects.forEach {
                        it.state = 2
                        objectDao.insObject(it)
                    }
                    qrCodes.forEach {
                        it.state = 2
                        qrCodeDao.insQrCode(it)
                    }
                    relations.forEach {
                        it.state = 2
                        relationDao.insRelation(it)
                    }
                    problemArchs.forEach {
                        it.state = 2
                        problemArchDao.insProblem(it)
                    }
                    problemEquips.forEach {
                        it.state = 2
                        problemEquipDao.insProblem(it)
                    }
                }
            }
        } catch (e: Exception) {
            println("uploadBuilding Exception: $e")
        }
    }

    /**
     *  获取文件信息
     */
    suspend fun getFileUploadInfo(file: FileInfo): Model<FileUploadInfo> {
        return api.getFileUploadInfo(file = file)
    }

    /**
     * 上传文件
     */
    suspend fun uploadFile(url: String, file: RequestBody): String {
        return api.uploadFile(url, file)
    }

    /**
     * 获取文件下载信息
     */
    suspend fun getFileDownloadInfo(fileId: String): Model<FileDownloadInfo> {
        return api.getFileDownloadInfo(fileId = fileId)
    }

    /**
     * 下载地址
     */
    suspend fun downloadFile(fileUrl: String): String {
        return api.downloadFile(url = fileUrl)
    }

    /**
     * 获取 Client
     */
    suspend fun getClient(client: Client) {
        val model = api.getClient(client)
        sp.edit { putString(CLIENT_ID, model.data.clientId) }
    }

    /**
     * 登录
     */
    suspend fun login(login: Login): List<Long> {
        val json =
            """{"loginDevice":"${login.device}","loginName":"${login.userName}","password":"${login.password}"}"""
        var result = listOf<Long>()
        withContext(Dispatchers.IO) {
            request(
                from = { api.login(json) },
                to = {
                    val one = it[0]
                    val user = UserEntity(
                        userId = one.userId,
                        username = one.userName,
                        password = login.password.md5(),
                        pd = one.pd,
                        device = one.loginDevice,
                        isAdmin = one.isAdmin,
                        mail = one.person_mail,
                        phone = one.phone_num,
                        from = one.userFrom,
                        state = 1
                    )

                    val userDao = db.userDao()
                    val users = userDao.getUsers().onEach { u -> u.state = 0 }
                    users.add(user)
                    result = userDao.insUsers(users)
                },
                error = {
                    println("login error :$it")
                }
            )
        }
        return result
    }

    suspend fun getProjects(user: UserEntity) {
        val json =
            """{"puser":{"userId":"${user.userId}","loginDevice":"${user.device}","pd":"${user.pd}"}}"""
        withContext(Dispatchers.IO) {
            request(
                from = { api.getProjects(json) },
                to = { results ->
                    results.forEach {
                        it.projects.forEach { p ->
                            val project = ProjectEntity(
                                projectId = p.projectId,
                                projectName = p.projectName,
                                groupCode = p.groupCode,
                                latitude = p.latitude,
                                longitude = p.longitude,
                                detailAddress = p.detailAddress,
                                projectLocalID = p.projectLocalID,
                                projectLocalName = p.projectLocalName,
                                functionType = p.functionType,
                                liveStatus = p.liveStatus,
                                userId = it.userId
                            )
                            val projectDao = db.projectDao()
                            projectDao.insProject(project)
                        }
                    }
                },
                error = {
                    println("getProjects error: $it")
                }
            )
        }
    }

    suspend fun getProjectsFromDB(): List<ProjectEntity> {
        return withContext(Dispatchers.IO) {
            db.projectDao().getProjects()
        }
    }

    suspend fun getUser(username: String, password: String): UserEntity? {
        return withContext(Dispatchers.IO) {
            db.userDao().getUser(username, password.md5())
        }
    }

    // 获取数据库用户
    suspend fun getUsers(): List<UserEntity> {
        return withContext(Dispatchers.IO) {
            db.userDao().getUsers()
        }
    }

    // 根据登录状态获取User
    suspend fun getUserByState(state: Int): UserEntity {
        return withContext(Dispatchers.IO) {
            db.userDao().getUserByState(state)
        }
    }

}