import {nanoid} from "nanoid";

export enum TASK_ERRORS {
    INVALID_STATUS = "TASK_ERRORS_INVALID_STATUS"
}

export enum TASK_STATUS {
    READY = "READY",
    MOVING = "MOVING",
    PROCESSING = "PROCESSING",
    SUCCESS = "SUCCESS",
    FAIL = "FAIL"
}

export declare interface TaskConfig {
    level?: number,
    isPreCondition?: boolean,
    parentTaskId?: string,
    asyncFunction: (Task) => Promise<any | void>,
    onReady?: (task: Task, message?: any) => Promise<any | void>
    onMoving?: (task: Task, message?: any) => Promise<any | void>
    onSuccess?: (task: Task, message?: any) => Promise<any | void>
    onFail?: (task: Task, message?: any) => Promise<any | void>
    onProcessing?: (task: Task, message?: any) => Promise<any | void>
}

const generateTaskId = () => {
    if (typeof crypto !== "undefined" && crypto && crypto?.randomUUID) return crypto.randomUUID() + crypto.randomUUID()
    return nanoid(72)//兼容RN端
}

/**
 * ## Task **任务实体**
 * parentTaskId用来处理多层级任务的问题
 * 例如 如果父级任务是前置任务 且 包含了多个子级任务  这个时候 子级任务是永远不会执行 因为有且只能有一个前置任务
 * 前置任务里面的子级任务排在前置任务后面 所以不会执行
 * 这个时候需要用这个parentTaskId进行关系绑定
 * **/
export class Task {
    /** 任务ID **/
    public readonly id: string = generateTaskId()
    /** 父级任务ID **/
    public readonly parentTaskId: string = ""
    /** 任务执行的异步函数 **/
    public readonly asyncFunction: (Task) => Promise<any | void> = async () => ({})
    /** 任务优先等级 **/
    public readonly level: number = 0
    /** 任务失败次数 **/
    public failTimes: number = 0
    /** 任务执行成功结果 **/
    public result: Array<any> = []
    /** 任务执行失败报错信息 **/
    public error: any = undefined
    /** 任务状态 **/
    public status: TASK_STATUS = TASK_STATUS.READY
    /* 是否前置条件任务 如果为true的时候 无论设置并发数量多少 都会先执行这个任务(多个前置条件任务按照level排序) 且当前只执行该任务 */
    public isPreCondition: boolean = false

    /** 任务准备状态回调 会被触发多次 **/
    private readonly onReady: (task: Task, message?: any) => Promise<any | void> = async () => ({})
    /** 任务被移动的回调 移动期间不允许对任务进行其他操作  例如从成功的队列移动到失败的队列 **/
    private readonly onMoving: (task: Task, message?: any) => Promise<any | void> = async () => ({})
    /** 任务执行成功回调 **/
    private readonly onSuccess: (task: Task, message?: any) => Promise<any | void> = async () => ({})
    /** 任务执行失败回调 **/
    private readonly onFail: (task: Task, message?: any) => Promise<any | void> = async () => ({})
    /** 任务执行中的回调 **/
    private readonly onProcessing: (task: Task, message?: any) => Promise<any | void> = async () => ({})

    constructor(config: TaskConfig) {
        const {
            asyncFunction,
            level,
            onReady,
            onProcessing,
            onSuccess,
            onFail,
            onMoving,
            isPreCondition,
            parentTaskId
        } = config

        this.asyncFunction = asyncFunction ?? this.asyncFunction
        this.level = level ?? this.level

        this.onReady = onReady ?? this.onReady
        this.onProcessing = onProcessing ?? this.onProcessing
        this.onSuccess = onSuccess ?? this.onSuccess
        this.onFail = onFail ?? this.onFail
        this.onMoving = onMoving ?? this.onMoving

        this.isPreCondition = isPreCondition ?? false

        this.parentTaskId = parentTaskId ?? this.parentTaskId

        this.updateStatus(TASK_STATUS.READY)
    }

    /** 任务状态更新 **/
    public async updateStatus(status: TASK_STATUS, message?: any): Promise<void> {
        this.status = status

        switch (status) {
            case TASK_STATUS.READY:
                this.result = []
                this.error = undefined
                this.onReady(this, message)
                break
            case TASK_STATUS.MOVING:
                this.onMoving(this, message)
                break
            case TASK_STATUS.PROCESSING:
                this.result = []
                this.error = undefined
                this.onProcessing(this, message)
                break
            case TASK_STATUS.SUCCESS:
                this.result = message ?? []
                this.onSuccess(this, ...(message ?? []))
                break
            case TASK_STATUS.FAIL:
                await this.addFailTimes()
                this.error = message
                this.onFail(this, message)
                break
        }
    }

    /** 失败次数增加 **/
    private async addFailTimes(): Promise<void> {
        this.failTimes++
    }

    /** 执行任务 **/
    public async execute(): Promise<any> {
        if (this.status !== TASK_STATUS.READY) throw new Error(TASK_ERRORS.INVALID_STATUS)

        await this.updateStatus(TASK_STATUS.PROCESSING)
        let result: any = []

        try {
            await this.asyncFunction(this).then((...arg) => {
                result = arg
            })
            await this.updateStatus(TASK_STATUS.SUCCESS, result)
        } catch (error) {
            await this.updateStatus(TASK_STATUS.FAIL, error)
            throw error instanceof Error ? error as Error : new Error(error as string)
        }

        return result
    }
}
