import {AiTaskStatuses, CommonTask, RefResource, TaskFrom, TaskStatuses, TaskTypes} from "~/types";
import request from "~/utils/request";
import useTask from "~/store/task";
import {reactive} from "vue";
import useSession from "~/store/session";

const store = reactive<{
    dataset: {
        [key: string]: RefResource
    },
    running: Set<string>
}>({
    dataset: {}, running: new Set()
})

export default function useRefResource() {

    const { getTaskStatus, getTask, getTaskResource, isTaskFinished } = useTask();
    const { sync, exists } = useSession()
    const push = (resource: RefResource) => {
        if (!resource.resource_url) return;
        store.dataset[resource.resource_url] = resource;
    }

    const normalize_resource_url = (value?: string) => (value || '').split('?')[0].trim();

    const resolve_resource_by_url = (url: string): RefResource | undefined => {
        const direct = store.dataset[url];
        if (direct) return direct;
        const normalized = normalize_resource_url(url);
        if (!normalized) return undefined;
        const keyMatch = Object.keys(store.dataset).find(k => normalize_resource_url(k) === normalized);
        if (keyMatch) return store.dataset[keyMatch];
        return Object.values(store.dataset).find(r => normalize_resource_url(r.resource_url) === normalized);
    }

    const get_task_result = (dbTask: CommonTask) => {
        try {
            if (dbTask.payload) {
                let pl = JSON.parse(dbTask.payload.payload);
                if (pl) {
                    // time from task server may have more accurate value (9 digits after dot)
                    // and 2 time values may be very close, same in millisecond level
                    // in this case we can only use the origin string to do comparison
                    // since Javascript only accurate to the millisecond level.
                    let last = Object.entries(pl).map(([k, v]) => ({time: k, value: v as any}))
                        .filter(x => !isNaN(new Date(x.time).valueOf()))
                        .reduce((a, c) => c.value?.task_result ? c : (c.time > a.time ? c : a), {time: "0", value: null})
                    if (last && last.value) {
                        return last.value.task_result?.task_result || last.value.task_result
                    }
                }
            }
        } catch (e) {
            console.log("get task result failed...", e, dbTask.payload)
        }
    }

    const check_resource_task = async (resource: RefResource, index: number, resources: RefResource[]) => {
        try {
            push(resource)
            if (resource.tasks?.vals?.length) {
                for (let i = 0; i < resource.tasks?.vals.length; i++) {
                    let task = resource.tasks.vals[i]
                    if(task && task.task_id && task.task_from !== TaskFrom.AI && !isTaskFinished(task.task_status)) {
                        let dbTask = await getTask(task.task_id);
                        if (dbTask && dbTask.task_status && dbTask.task_status !== task.task_status) {
                            task.task_status = dbTask.task_status;
                            if (dbTask.task_status === TaskStatuses.DONE) {
                                let res = await getTaskResource(task.task_id);
                                if (resource && res) {
                                    resource.hty_resource_id = res.hty_resource_id;
                                    resource.resource_url = res.url?.trim();
                                    resources[index] = {...resource}
                                }
                                // try to get db task result and sync to task
                                let result = get_task_result(dbTask);
                                if (result) {
                                    task.task_result = result
                                }
                            }
                            await request({url: "/api/v1/ws/update_ref_resource", method: "POST", data: resource})
                            if (exists()) {
                                await sync();
                            }
                        }
                    }
                }
            }
        } catch (e) {
            console.error("check resource task failed...", e)
        }
    }

    const check_resources_task = async (resources?: RefResource[]) => {
        resources?.forEach(check_resource_task)
    }

    const watermark = async (url: string, user_id: string, ref_resource_id: string) => {
        const {r, d, e} = await request({
            url: '/api/v2/ts/create_task', method: 'POST',
            data: {task_type: TaskTypes.WATERMARK, payload: {video_url: url, user_id, ref_resource_id}}
        });
        if (r) return d;
    }

    /** 任一条 WATERMARK 已落库 URL 时直接可用（避免仅 TS 条 task_result=null 挡住另一条带 marked_url / watermarked_video_url 的记录） */
    const extract_watermark_url_from_vals = (taskList: CommonTask[] | undefined): string | undefined => {
        const list = (taskList || []).filter(t => t.task_type === TaskTypes.WATERMARK);
        for (let i = list.length - 1; i >= 0; i--) {
            const t = list[i];
            if (!t?.task_result) continue;
            const r: any = (t.task_result as any).task_result || t.task_result;
            const u = r?.watermarked_video_url ?? r?.marked_url;
            if (u) return String(u).replace(/"/g, '').trim();
        }
        return undefined;
    };

    const recur_check = async (url: string, user_id: string): Promise<string | undefined> => {
        let resource = resolve_resource_by_url(url);
        if (!resource) return undefined;
        const resourceKey = resource.id || normalize_resource_url(resource.resource_url || url);
        let tasks = (resource.tasks?.vals || []).filter(t => t.task_type === TaskTypes.WATERMARK);
        const from_embedded = extract_watermark_url_from_vals(resource.tasks?.vals);
        if (from_embedded) return from_embedded;

        // local cache may lag behind DB (e.g. only TS row with null task_result). refresh once from ws.
        if (resource.id) {
            const fresh = await request({url: `/api/v1/ws/find_ref_resource_by_id/${resource.id}`});
            if (fresh.r && fresh.d) {
                resource = fresh.d;
                push(resource);
                const refreshed = extract_watermark_url_from_vals(resource.tasks?.vals);
                if (refreshed) return refreshed;
                tasks = (resource.tasks?.vals || []).filter(t => t.task_type === TaskTypes.WATERMARK);
            }
        }

        let task_submitted = false;
        if (tasks.length > 0) {
            let tsTasks = tasks.filter(t => t.task_from === TaskFrom.TaskServer);
            if (tsTasks.length > 0) {
                task_submitted = true;
                if (tsTasks.some(t => !isTaskFinished(t.task_status))) {
                    store.running.add(resourceKey);
                } else {
                    store.running.delete(resourceKey);
                }
                let doneTasks = tsTasks.filter(t => t.task_status === TaskStatuses.DONE)
                if (doneTasks.length) {
                    let aiTask = tasks.find(t => t.task_from === TaskFrom.AI && t.task_status === AiTaskStatuses.SUCCESS)
                    if (aiTask) {
                        // @ts-ignore
                        return aiTask.task_result["watermarked_video_url"] || aiTask.task_result["marked_url"]
                    }
                    let result = doneTasks.find(x => x.task_result)?.task_result;
                    if (result) {
                        return result.watermarked_video_url || result.marked_url;
                    }
                    // DONE but task_result missing: query task details once to recover embedded url result.
                    for (let i = doneTasks.length - 1; i >= 0; i--) {
                        const doneTask = doneTasks[i];
                        if (!doneTask.task_id) continue;
                        const dbTask = await getTask(doneTask.task_id);
                        if (!dbTask) continue;
                        const recovered = get_task_result(dbTask);
                        if (recovered) {
                            doneTask.task_result = recovered as any;
                            await request({url: "/api/v1/ws/update_ref_resource", method: "POST", data: resource});
                            return (recovered as any).watermarked_video_url || (recovered as any).marked_url;
                        }
                    }
                }

                if (tsTasks.every(t => t.task_status === TaskStatuses.FAILED)) {
                    // retry
                    store.running.delete(resourceKey);
                    task_submitted = false;
                } else if (tsTasks.every(t => isTaskFinished(t.task_status))) {
                    // all finished but still no usable url -> allow re-submit watermark task
                    store.running.delete(resourceKey);
                    task_submitted = false;
                } else {
                    // read new status
                    let task_id = tsTasks.find(t => !isTaskFinished(t.task_status))?.task_id as string | undefined;
                    if (!task_id) return undefined;
                    let status = await getTaskStatus(task_id);
                    if (!isTaskFinished(status)) {
                        // Non-blocking behavior for repeated clicks:
                        // keep showing "processing" state to user and return immediately,
                        // instead of waiting in a recursive polling loop until watermark finishes.
                        return undefined;
                    } else {
                        let task = await getTask(task_id);
                        // @ts-ignore
                        resource.tasks.vals.forEach((t, i) => {
                            if (t.task_id === task_id) {
                                // @ts-ignore
                                resource.tasks.vals[i] = task;
                            }
                        })
                        let result = get_task_result(task);
                        if (result) {
                            task.task_result = result
                        }
                        store.running.delete(resourceKey);
                        return result?.watermarked_video_url || result?.marked_url;
                    }
                }
            }
        }
        if (store.running.has(resourceKey)) {
            return undefined;
        }
        if (!task_submitted) {
            let task_id = await watermark(resource.resource_url as string, user_id, resource.id as string);
            if (task_id) {
                store.running.add(resourceKey);
                if (!resource.tasks?.vals?.length) {
                    resource.tasks = {vals: []}
                }
                resource.tasks.vals.push({task_id, created_at: new Date(), task_status: TaskStatuses.PENDING, task_type: TaskTypes.WATERMARK, task_from: TaskFrom.TaskServer})
                await request({url: "/api/v1/ws/update_ref_resource", method: "POST", data: resource})
                if (exists()) {
                    await sync();
                }
            } else {
                store.running.delete(resourceKey);
            }
        }
    }

    const download_resource = async (url: string, user_id: string) => {
        return await recur_check(url, user_id)
    }

    const get_hty_resource_by_id = async (id: string) => {
        const {r, d, e} = await request({
            url: `/api/v1/uc/find_hty_resource_by_id/${id}`, method: 'GET'
        })
        if (r) {
            return d;
        }
    }

    return {
        check_resource_task, check_resources_task, download_resource, get_hty_resource_by_id
    }
}
