package com.framework.mvvm.mv

import android.content.SharedPreferences
import android.util.Log
import androidx.core.content.edit
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import cn.sagacloud.cadengine.OkhttpUtil
import com.framework.app.*
import com.framework.app.http.FileInfo
import com.framework.app.http.FileUploadInfo
import com.framework.app.tools.md5
import com.framework.app.tools.toBytes
import com.framework.mvvm.model.data.Data
import com.framework.mvvm.model.data.Response
import com.framework.mvvm.model.data.SuccessResponse
import com.framework.mvvm.model.data.request
import com.framework.mvvm.model.db.entity.ProjectEntity
import com.framework.mvvm.model.db.entity.UserEntity
import com.framework.mvvm.model.db.entity.task.ObjectEntity
import com.framework.mvvm.model.repository.AdmRepository
import com.framework.mvvm.model.vo.AdmRequest
import com.framework.mvvm.model.vo.Building
import com.framework.mvvm.model.vo.Client
import com.framework.mvvm.model.vo.Login
import com.sybotan.android.demo.DemoApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import java.io.File
import java.io.FileOutputStream


class AdmViewModel(private val repository: AdmRepository) : ViewModel() {

    /**
     * 请求字典数据
     */
    fun requestDict(request: AdmRequest, block: (() -> Unit)? = null) {
        viewModelScope.launch {
            repository.requestDict(request)
            block?.invoke()
        }
    }

    /**
     * 请求配置信息
     */
    fun requestConfig(request: AdmRequest, block: (() -> Unit)? = null) {
        viewModelScope.launch {
            repository.requestConfig(request)
            block?.invoke()
        }
    }

    /**
     * 请求框架数据
     */
    fun requestFrame(request: AdmRequest, block: (() -> Unit)? = null) {
        viewModelScope.launch {
            repository.requestFrame(request)
            block?.invoke()
        }
    }

    /**
     * 上传建筑数据
     */
    fun uploadBuilding(
        request: AdmRequest,
        block: (() -> Unit)? = null,
        log: ((String, Building?) -> Unit)? = null
    ) {
        viewModelScope.launch(Dispatchers.IO) {
            //val buildings = repository.getBuildings()
            val buildings = repository.getBuildingByProjectId(request.projectId)
            if (buildings.isEmpty()) return@launch
            for (building in buildings) {
                repository.uploadBuilding(
                    userId = request.userId,
                    groupCode = building.groupCode!!,
                    projectId = building.projectId!!,
                    buildingId = building.id,
                    log = log
                )
            }
            withContext(Dispatchers.Main) {
                block?.invoke()
            }
        }
    }

    /**
     *  下载建筑数据
     */
    fun downloadBuilding(
        request: AdmRequest,
        sp: SharedPreferences,
        isProject: Boolean = false,
        block: (() -> Unit)? = null
    ) {
        viewModelScope.launch(Dispatchers.IO) {
            //val buildings = repository.getBuildings()
            val buildings = repository.getBuildingByProjectId(request.projectId)
            println("downloadBuilding will download : ${buildings.size}")
            if (buildings.isEmpty()) {
                Log.d("downloadBuilding", "buildings is empty")
                return@launch
            }
            val job = launch { // 启动一个总协程
                for (building in buildings) {
                    launch {  // 为每个下载任务启动协程
                        repository.requestBuilding(
                            AdmRequest(
                                userId = request.userId,
                                projectId = building.projectId!!,
                                groupCode = building.groupCode!!,
                                buildingId = if (isProject) "" else building.id,
                                clientId = sp.getString(CLIENT_ID, "") ?: "",
                                bdtpDownloadTs = sp.getLong(
                                    "${
                                        if (isProject)
                                            building.projectId
                                        else
                                            building.id
                                    }.$BDTP_TS",
                                    0L
                                ),
                                admDownloadTs = sp.getLong(
                                    "${
                                        if (isProject)
                                            building.projectId
                                        else
                                            building.id
                                    }.$ADM_TS",
                                    0L
                                )
                            )
                        ) {
                            Log.e(
                                "downloadBuilding",
                                "bdtpDownloadTs:${it.bdtpDownloadTs}, admDownloadTs:${it.admDownloadTs}"
                            )

                            sp.edit {
                                putLong(
                                    "${if (isProject) building.projectId else building.id}.$BDTP_TS",
                                    it.bdtpDownloadTs
                                )
                            }

                            sp.edit {
                                putLong(
                                    "${if (isProject) building.projectId else building.id}.$ADM_TS",
                                    it.admDownloadTs
                                )
                            }
                        }
                    }
                }
            }
            job.join()

            // 下载地图
            val gzipJob = launch {
                val floors = repository.getFloors()
                floors.forEach {
                    launch(Dispatchers.IO) {
                        val json = it.infos?.asJsonObject
                        val floorMap = json?.get("floorMap")?.asString ?: ""
                        if (floorMap.isNotEmpty()) {
                            val mapString = OkhttpUtil.getGzipMap("$IP$pathDownloadMap${floorMap}")
                            val mapFile =
                                File(DemoApp.getInstance()?.getExternalFilesDir("floorMap"), it.id)
                            FileOutputStream(mapFile).use { output -> output.write(mapString.toByteArray()) }
                            it.floorMap = mapFile.absolutePath
                            repository.updateObject(it)
                        }
                    }
                }
            }
            gzipJob.join()
            withContext(Dispatchers.Main) {
                block?.invoke()
            }
        }
    }

    /**
     * 获取建筑数量
     */
    val buildings: StateFlow<List<ObjectEntity>> = flow {
        emit(repository.getBuildings())
    }.stateIn(
        scope = viewModelScope,
        started = WhileSubscribed(5000),
        initialValue = arrayListOf()
    )

    /**
     * 获取本地数据中的建筑信息
     */
    suspend fun getBuildings(): List<ObjectEntity> {
        return repository.getBuildings()
    }

    /**
     * 获取文件上传信息
     */
    private suspend fun getFileUploadInfo(basic: FileInfo): Response<FileUploadInfo> {
        return request { repository.getFileUploadInfo(basic) }
    }

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

    /**
     * 上传文件
     */
    fun fileUpload(file: File) {
        val bytes = file.toBytes()

        val md5 = md5(bytes)

        val info = FileInfo(
            fileMd5 = md5,
            fileName = file.name,
            fileSize = bytes.size
        )

        viewModelScope.launch(Dispatchers.IO) {
            val response = getFileUploadInfo(info)
            if (response is SuccessResponse) {
                val data = response.data
                val url = data.content?.get(0)?.uploadUrl ?: ""
                if (url.isNotEmpty()) {
                    val body = bytes.toRequestBody()
                    val result = uploadFile(url, body)

                    Log.d("fileUpload", "success: $result")
                }
            } else {
                Log.d("fileUpload", "upload file has something error：$response")
            }
        }
    }

    /**
     * 获取服务端生成的客户端Id
     */
    fun getClient(client: Client) {
        viewModelScope.launch {
            repository.getClient(client)
        }
    }

    /**
     * 登录
     */
    suspend fun login(login: Login): List<Long> {
        val def = viewModelScope.async {
            repository.login(login)
        }
        return def.await()
    }

    suspend fun getUser(username: String, password: String): UserEntity? {
        val def = viewModelScope.async {
            repository.getUser(username, password)
        }
        return def.await()
    }

    suspend fun getUsers(): List<UserEntity> {
        return repository.getUsers()
    }

    suspend fun getProjects(): List<ProjectEntity> {
        val user = repository.getUserByState(1)
        repository.getProjects(user)
        return repository.getProjectsFromDB()

    }

    suspend fun getCurrentUser(): UserEntity {
        return repository.getUserByState(1)
    }

    suspend fun getNoDoneTaskByProjectId(projectId: String): Int {
        return repository.unUploadTaskByProjectId(projectId)
    }
}