import { ElNotification } from 'element-plus'
import { defineStore } from 'pinia'
import { nextTick, reactive } from 'vue'
import { buildTerminalUrl } from '/@/api/common'
import { i18n } from '/@/lang/index'
import { STORE_TERMINAL } from '/@/stores/constant/cacheKey'
import { SYSTEM_ZINDEX } from '/@/stores/constant/common'
import { taskStatus } from '/@/stores/constant/terminalTaskStatus'
import type { Terminal } from '/@/stores/interface/index'
import { timeFormat } from '/@/utils/common'
import { uuid } from '/@/utils/random'
import { closeHotUpdate, openHotUpdate } from '/@/utils/vite'

export const useTerminal = defineStore(
    'terminal',
    () => {
        const state: Terminal = reactive({
            show: false,
            showDot: false,
            taskList: [],
            packageManager: 'pnpm',
            showConfig: false,
            automaticCleanupTask: '1',
            phpDevelopmentServer: false,
            npmRegistry: 'unknown',
            composerRegistry: 'unknown',
        })

        function init() {
            for (const key in state.taskList) {
                if (state.taskList[key].status == taskStatus.Connecting || state.taskList[key].status == taskStatus.Executing) {
                    state.taskList[key].status = taskStatus.Unknown
                }
            }
        }

        function toggle(val = !state.show) {
            state.show = val
            if (val) {
                toggleDot(false)
            }
        }

        function toggleDot(val = !state.showDot) {
            state.showDot = val
        }

        function toggleConfigDialog(val = !state.showConfig) {
            toggle(!val)
            state.showConfig = val
        }

        function changeRegistry(val: string, type: 'npm' | 'composer') {
            state[type == 'npm' ? 'npmRegistry' : 'composerRegistry'] = val
        }

        function changePackageManager(val: string) {
            state.packageManager = val
        }

        function changePHPDevelopmentServer(val: boolean) {
            state.phpDevelopmentServer = val
        }

        function changeAutomaticCleanupTask(val: '0' | '1') {
            state.automaticCleanupTask = val
        }

        function setTaskStatus(idx: number, status: number) {
            state.taskList[idx].status = status
            if ((status == taskStatus.Failed || status == taskStatus.Unknown) && state.taskList[idx].blockOnFailure) {
                setTaskShowMessage(idx, true)
            }
        }

        function taskCompleted(idx: number) {
            // 命令执行完毕，重新打开热更新
            openHotUpdate('terminal')

            if (typeof state.taskList[idx].callback != 'function') return

            const status = state.taskList[idx].status
            if (status == taskStatus.Failed || status == taskStatus.Unknown) {
                state.taskList[idx].callback(taskStatus.Failed)
            } else if (status == taskStatus.Success) {
                state.taskList[idx].callback(taskStatus.Success)
            }
        }

        function setTaskShowMessage(idx: number, val = !state.taskList[idx].showMessage) {
            state.taskList[idx].showMessage = val
        }

        function addTaskMessage(idx: number, message: string) {
            if (!state.show) toggleDot(true)
            state.taskList[idx].message = state.taskList[idx].message.concat(message)
            nextTick(() => {
                execMessageScrollbarKeep(state.taskList[idx].uuid)
            })
        }

        function addTask(command: string, blockOnFailure = true, extend = '', callback: Function = () => {}) {
            const duplicatePending = state.taskList.some(
                (item) =>
                    item.command === command &&
                    (item.status === taskStatus.Waiting ||
                        item.status === taskStatus.Connecting ||
                        item.status === taskStatus.Executing)
            )
            if (duplicatePending) {
                return
            }
            if (!state.show) toggleDot(true)
            state.taskList = state.taskList.concat({
                uuid: uuid(),
                createTime: timeFormat(),
                status: taskStatus.Waiting,
                command: command,
                message: [],
                showMessage: false,
                blockOnFailure: blockOnFailure,
                extend: extend,
                callback: callback,
            })

            // 清理任务列表
            if (parseInt(state.automaticCleanupTask) === 1) {
                clearSuccessTask()
            }

            // 检查是否有已经失败的任务
            if (state.show === false) {
                for (const key in state.taskList) {
                    if (state.taskList[key].status == taskStatus.Failed || state.taskList[key].status == taskStatus.Unknown) {
                        ElNotification({
                            type: 'error',
                            message: i18n.global.t('terminal.Newly added tasks will never start because they are blocked by failed tasks'),
                            zIndex: SYSTEM_ZINDEX,
                        })
                        break
                    }
                }
            }

            startTask()
        }

        function addTaskPM(command: string, blockOnFailure = true, extend = '', callback: Function = () => {}) {
            addTask(command + '.' + state.packageManager, blockOnFailure, extend, callback)
        }

        function delTask(idx: number) {
            if (state.taskList[idx].status != taskStatus.Connecting && state.taskList[idx].status != taskStatus.Executing) {
                state.taskList.splice(idx, 1)
            }
            startTask()
        }

        function startTask() {
            let taskKey = null

            // 寻找可以开始执行的命令
            for (const key in state.taskList) {
                if (state.taskList[key].status == taskStatus.Waiting) {
                    taskKey = parseInt(key)
                    break
                }
                if (state.taskList[key].status == taskStatus.Connecting || state.taskList[key].status == taskStatus.Executing) {
                    break
                }
                if (state.taskList[key].status == taskStatus.Success) {
                    continue
                }
                if (state.taskList[key].status == taskStatus.Failed || state.taskList[key].status == taskStatus.Unknown) {
                    if (state.taskList[key].blockOnFailure) {
                        break
                    } else {
                        continue
                    }
                }
            }
            if (taskKey !== null) {
                setTaskStatus(taskKey, taskStatus.Connecting)
                startEventSource(taskKey)
            }
        }

        function startEventSource(taskKey: number) {
            // 命令执行期间禁用热更新
            closeHotUpdate('terminal')

            window.eventSource = new EventSource(
                buildTerminalUrl(state.taskList[taskKey].command, state.taskList[taskKey].uuid, state.taskList[taskKey].extend)
            )
            window.eventSource.onmessage = function (e) {
                const data = JSON.parse(e.data)
                if (!data || !data.data) {
                    return
                }

                const taskIdx = findTaskIdxFromUuid(data.uuid)
                if (taskIdx === false) {
                    return
                }

                if (data.data == 'command-exec-error') {
                    setTaskStatus(taskIdx, taskStatus.Failed)
                    window.eventSource.close()
                    taskCompleted(taskIdx)
                    startTask()
                } else if (data.data == 'command-exec-completed') {
                    window.eventSource.close()
                    if (state.taskList[taskIdx].status != taskStatus.Success) {
                        setTaskStatus(taskIdx, taskStatus.Failed)
                    }
                    taskCompleted(taskIdx)
                    startTask()
                } else if (data.data == 'command-link-success') {
                    setTaskStatus(taskIdx, taskStatus.Executing)
                } else if (data.data == 'command-exec-success') {
                    setTaskStatus(taskIdx, taskStatus.Success)
                } else {
                    addTaskMessage(taskIdx, data.data)
                }
            }
            window.eventSource.onerror = function () {
                window.eventSource.close()
                const taskIdx = findTaskIdxFromGuess(taskKey)
                if (taskIdx === false) return
                setTaskStatus(taskIdx, taskStatus.Failed)
                taskCompleted(taskIdx)
            }
        }

        function retryTask(idx: number) {
            state.taskList[idx].message = []
            setTaskStatus(idx, taskStatus.Waiting)
            startTask()
        }

        function clearSuccessTask() {
            state.taskList = state.taskList.filter((item) => item.status != taskStatus.Success)
        }

        function findTaskIdxFromUuid(uuid: string) {
            for (const key in state.taskList) {
                if (state.taskList[key].uuid == uuid) {
                    return parseInt(key)
                }
            }
            return false
        }

        function findTaskIdxFromGuess(idx: number) {
            if (!state.taskList[idx]) {
                let taskKey = -1
                for (const key in state.taskList) {
                    if (state.taskList[key].status == taskStatus.Connecting || state.taskList[key].status == taskStatus.Executing) {
                        taskKey = parseInt(key)
                    }
                }
                return taskKey === -1 ? false : taskKey
            } else {
                return idx
            }
        }

        function execMessageScrollbarKeep(uuid: string) {
            const execMessageEl = document.querySelector('.exec-message-' + uuid) as Element
            if (execMessageEl && execMessageEl.scrollHeight) {
                execMessageEl.scrollTop = execMessageEl.scrollHeight
            }
        }

        return {
            state,
            init,
            toggle,
            toggleDot,
            setTaskStatus,
            setTaskShowMessage,
            addTaskMessage,
            addTask,
            addTaskPM,
            delTask,
            startTask,
            retryTask,
            clearSuccessTask,
            toggleConfigDialog,
            changeRegistry,
            changePackageManager,
            changePHPDevelopmentServer,
            changeAutomaticCleanupTask,
        }
    },
    {
        persist: {
            key: STORE_TERMINAL,
            pick: ['state.showDot', 'state.taskList', 'state.automaticCleanupTask', 'state.npmRegistry', 'state.composerRegistry'],
        },
    }
)
