import {reactive} from 'vue';
import request from "../utils/request";
import {showFailToast, showSuccessToast} from "vant";
import dayjs from "dayjs";
import {
    Comment,
    CommentRefTypes,
    Daka,
    DakaScope,
    HtyBaseRoles,
    HtyRoles,
    Clazz,
    JihuaQueryParam,
    Lianxi, MultiVals,
    NotifyParam,
    NotifyTypes,
    Piyue,
    CourseSection, TeacherSummary
} from '~/types'
import useUser from "~/store/user";
import useTeacher from "~/store/teacher";
import useRefResource from "~/store/ref-resource";
import {formatDate} from "~/utils";
import useLoading from "~/store/loading";
import useLianxi from "~/store/lianxi";
import useComment from "~/store/comment";
import usePiyue from "~/store/piyue";
import useNotify from "~/store/notify";
import useCourseCategory from "~/store/qumu-category";

const initialDaka = () => ({
    start_date: dayjs().startOf('day').toDate(),
    end_date: dayjs().add(6, 'day').startOf('day').toDate(),
    duration_days: 7,
    course_sections: [] as CourseSection[]
} as Daka);

export interface DakaState {
    current: Daka,
    list: Daka[],
    total: number,
    pages: number,
    hanging: boolean,
    checking: boolean,
    current_lianxi?: Lianxi,
    query_cache?: JihuaQueryParam,
}

const store = reactive<DakaState>({
    current: initialDaka(),
    list: [],
    total: 0,
    pages: 0,
    hanging: false,
    checking: false
});


export default function useDaka() {

    const usingUser = useUser();
    const {create_lianxi, remove_lianxi, check_lianxi_tasks} = useLianxi();
    const {create_piyue} = usePiyue();
    const {create_comment, load_comments, check_comments_task, get_resource_note_group, get_resource_note_group_by_ref_id, check_resource_notes_task} = useComment()
    const {notify_ws} = useNotify();
    const {get_hty_resource_by_id} = useRefResource();
    const usingQC = useCourseCategory()
    usingQC.query();
    const notify = (params: NotifyParam, role_key: HtyRoles) =>  notify_ws(params, role_key);
    const {load_start, load_done} = useLoading()

    function getEndDate({start_date, duration_days}: Daka) {
        let end = dayjs(start_date).add(duration_days, 'day');
        return formatDate(end);
    }
    function reset() {
        if (!store.hanging) {
            store.current = initialDaka();
        }
        store.hanging = false;
    }

    async function query(params: JihuaQueryParam): Promise<boolean> {
        let {current: {hty_id}, currentRole} = usingUser.store;
        if (currentRole === HtyBaseRoles.TEACHER) {
            params.teacher_id = hty_id;
            params.scope = DakaScope.ALL;
        } else if (currentRole === HtyBaseRoles.STUDENT) {
            params.student_id = hty_id;
            const { store: teacherStore } = useTeacher();
            if (teacherStore.currentTeacherId) {
                params.teacher_id = teacherStore.currentTeacherId;
            }
        }
        store.query_cache = params;
        load_start()
        const {r, d, e} = await request({url: '/api/v1/ws/find_dakas_with_sections_by_user_id', params});
        load_done()
        if (r) {
            if (params.page === 1) {
                store.list = [];
            }
            let [list, pages, total] = d;
            let kcs = await queryClazzByDakaIds(list.map((x: Daka) => x.id))
            let dict: {[key: string]: Clazz} = {};
            kcs.forEach(kc => {
                kc.dakas?.vals.forEach(x => {
                    dict[x.id as string] = kc;
                })
            })
            store.list = [...store.list, ...list.map((x: Daka) => ({...convert(x), clazz: dict[x.id], end_date: getEndDate(x)}))];
            store.total = total;
            store.pages = pages;
        } else {
            showFailToast(e);
        }
        return Promise.resolve(r);
    }


    async function queryClazzByDakaIds(daka_ids: string[]): Promise<Clazz[]> {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/clazz/find_by_daka_ids', data: daka_ids, method: 'POST'
        });
        load_done()
        if (r) {
            return d || [];
        } else {
            return []
        }
    }


    async function get_qs_resource_note_groups() {
        await Promise.all(store.current.course_sections.map(async (qs) => {
            if (!qs.resource_note_group) {
                await get_resource_note_group(qs)
            } else {
                await check_resource_notes_task(qs.resource_note_group)
            }
        }))
    }

    async function check_tasks() {
        if (store.checking) return;
        store.checking = true;
        await check_comments_task(store.current.id)
        await check_lianxi_tasks(store.current.course_sections);
        store.checking = false;
    }


    const convert = (daka: Daka) => {
        let { course_sections, course_sections2 } = daka;
        let lianxiDict: { [key: string]: CourseSection } = {};
        course_sections.forEach(qs => {
            lianxiDict[qs.id as string] = qs
        })
        if (course_sections2?.vals?.length) {
            daka.course_sections = course_sections2.vals
            daka.course_sections.forEach(qs => {
                let tmp = lianxiDict[qs.id as string];
                qs.lianxis = tmp?.lianxis;
                qs.lianxi_count = tmp?.lianxi_count;
                qs.not_piyue_count = tmp?.not_piyue_count
            })
        }
        return daka;
    }

    async function read(id: string, forceReload = false) {
        if (!id) return;
        if (store.current.id === id && !forceReload) return;
        load_start()
        const {r, d, e} = await request({url: `/api/v1/ws/find_daka_by_id2/${id}`});
        if (r) {
            convert(d);
            store.current = {...d, end_date: getEndDate(d)};
            await Promise.all([
                load_comments(id),
                get_qs_resource_note_groups()
            ]);
        } else {
            reset()
            showFailToast(e);
        }
        load_done()
    }

    function valid() {
        let { name, start_date, students, course_sections } = store.current;
        if (!name) {
            showFailToast("请输入打卡名称！")
            return false;
        }
        if (!start_date) {
            showFailToast("请选择打卡起始日期！")
            return false;
        }
        if (!students?.val.users.vals.length) {
            showFailToast("请选择学生！")
            return false;
        }
        if (!course_sections?.length) {
            showFailToast('请添加课程！')
            return false;
        }
        return true
    }

    async function prepare(qs?: CourseSection) {
        let { course_sections, course_sections2, ...rest } = store.current

        console.log(rest)
        console.log(qs)
        let qsData: {vals: CourseSection[]} = {vals: []}
        for (let i = 0; i < course_sections.length; i++) {
            if (qs?.id === course_sections[i].id) {
                course_sections[i] = qs as CourseSection;
                store.current.course_sections[i] = qs as CourseSection;
            }
            let { resources, ref_resources, id, resource_note_group, ...rest } = course_sections[i];
            ref_resources = ref_resources || resources;
            if (!resource_note_group) {
                resource_note_group = await get_resource_note_group_by_ref_id(id as string)
            }
            qsData.vals.push({id, ref_resources, ...rest, resource_note_group})
        }

        return {...rest, course_sections: qsData, course_section_ids: course_sections.map(x => x.id)} as unknown as Daka
    }

    async function create(): Promise<string | undefined> {
        if (!valid()) return;
        load_start()
        let data = await prepare();
        if (!data) {
            load_done();
            return;
        }
        let { end_date, teachers, students, ...rest } = data;
        let teacher_id = usingUser.store.current.hty_id;
        let teacher_name = usingUser.store.current.real_name;
        let created_by = usingUser.store.current.hty_id;
        clean_teachers(teachers)
        let payload = {teacher_id, teacher_name, created_by, students, teachers, ...rest};
        const {r, d, e} = await request({
            url: `/api/v1/ws/create_daka`,
            data: {...payload},
            method: 'POST'
        })
        if (r) {
            showSuccessToast("打卡创建成功！");
            // notify students
            students?.val.users.vals.forEach(u => {
                notify({ hty_id: u.user_id, daka_id: d, notify_type: NotifyTypes.DakaCreate, remark: "请认真完成练习，按时提交" } as NotifyParam, HtyBaseRoles.STUDENT)
            })
            // notify piyue teacher
            if (teachers?.vals?.length) {
                teachers?.vals.forEach(t => {
                    notify({ hty_id: t.teacher_id, daka_id: d, notify_type: NotifyTypes.DakaCreate, remark: "请注意查阅学生练习" } as NotifyParam, HtyBaseRoles.TEACHER)
                })
            }
        } else {
            showFailToast(e);
        }
        load_done()
        return d;
    }

    function clean_teachers(teachers?: MultiVals<TeacherSummary>) {
        if (!teachers) return;
        // clean repeat teachers
        let teacher_ids = new Set(usingUser.store.current.hty_id), i = 0;
        while (i < teachers?.vals?.length) {
            let {teacher_id} = teachers.vals[i]
            if (teacher_ids.has(teacher_id)) {
                teachers.vals.splice(i, 1);
            } else {
                teacher_ids.add(teacher_id)
                i++
            }
        }
    }

    async function update(qs? :CourseSection) {
        if (!valid()) return Promise.reject()
        load_start()
        let data = await prepare(qs);
        if (!data) {
            load_done();
            return 0;
        }
        let { end_date, teachers, students, clazz, ...rest } = data;
        clean_teachers(teachers)
        const {r, e} = await request({
            url: `/api/v1/ws/update_daka`,
            data: {
                teachers, students, ...rest,
                updated_by: usingUser.store.current.hty_id,
                updated_at: new Date()
            },
            method: 'POST'
        });
        load_done()
        if (r) {
            showSuccessToast('保存成功');
            reset()
            // notify students
            students?.val.users.vals.forEach(u => {
                notify({ hty_id: u.user_id, daka_id: rest.id, notify_type: NotifyTypes.DakaUpdate } as NotifyParam, HtyBaseRoles.STUDENT)
            })
            // notify all piyue teachers
            if (teachers?.vals?.length) {
                teachers?.vals.forEach(t => {
                    notify({ hty_id: t.teacher_id, daka_id: rest.id, notify_type: NotifyTypes.DakaUpdate } as NotifyParam, HtyBaseRoles.TEACHER)
                })
            }
        } else {
            load_done()
            showFailToast(e);
        }
        return r;
    }

    async function remove() {
        load_start()
        let { id, students } = store.current
        const {r, e} = await request({
            url: `/api/v1/ws/delete_daka_by_id/${id}`,
            method: 'POST'
        });
        load_done()
        students?.val.users.vals.forEach(u => {
            notify({ hty_id: u.user_id, daka_id: id, notify_type: NotifyTypes.DakaDelete } as NotifyParam, HtyBaseRoles.STUDENT)
        })
    
        if (!r) {
            showFailToast(e);
        }
        return r;
    }

    async function get_shifan(daka_course_section_id?: string) {
        let relation = store.current.relations?.find(r => r.id === daka_course_section_id);
        let course_section = store.current.course_sections.find(qs => qs.id === relation?.course_section_id);
        let audio_type = course_section?.course_category_key || usingQC.getDefault()?.category_key;
        let shifan = (course_section?.ref_resources || course_section?.resources)?.find(r => r.is_shifan);
        if (shifan) {
            let url = shifan.resource_url;
            if (shifan.hty_resource_id) {
                let resource = await get_hty_resource_by_id(shifan.hty_resource_id);
                if (resource) {
                    url = resource.url;
                }
            }
            return { shifan_url: url, audio_type }
        }
        return;
    }

    async function create_daka_lianxi(data: Lianxi) {
        load_start()
        let shifan = await get_shifan(data.daka_course_section_id);
        let lianxi_id = await create_lianxi(data, shifan)
        if (lianxi_id) {
            await read(store.current.id, true);
        }
        load_done()
        return lianxi_id;
    }

    async function delete_daka_lianxi(data: Lianxi) {
        let relation = store.current.relations?.find(r => r.id === data.daka_course_section_id);
        let course_section = store.current.course_sections.find(qs => qs.id === relation?.course_section_id);
        const r = await remove_lianxi(data.id);
        notify({ hty_id: store.current.teacher_id, lianxi_id: data.id, course_name: course_section?.course_name, course_section_name: course_section?.section_name, notify_type: NotifyTypes.LianxiDelete } as NotifyParam, HtyBaseRoles.TEACHER)
        await read(store.current.id, true);
        return r;
    }

    async function create_daka_comment(comment: Comment) {
        // @ts-ignore
        let qs = store.current.course_sections.find(x => x.lianxis?.includes(store.current_lianxi));
        console.log('qs...', qs)
        let shifan_url = qs?.ref_resources?.find(r => r.is_shifan)?.resource_url;
        let audio_type = qs?.course_category_key;
        console.log('shifan url...', shifan_url, audio_type)
        const created = await create_comment(comment, {shifan_url, audio_type});
        if (created) {
            let piyues = store.current_lianxi?.piyues;
            if (comment.ref_type === CommentRefTypes.Piyue) {
                piyues?.forEach(x => {
                    if (x.id === comment.ref_id) {
                        if (!x.comments.includes(created)) {
                            x.comments.push(created)
                        }
                    }
                })
            } else if (comment.ref_type === CommentRefTypes.ResourceNote) {
                piyues?.forEach(x => {
                    x.resource_note_group?.resource_notes?.forEach(y => {
                        if (y.id === comment.ref_id) {
                            if (!y.comments?.includes(created)) {
                                y.comments?.push(created)
                            }
                        }
                    })
                })
            }
            return created
        }
    }

    async function create_daka_piyue(piyue: Piyue) {
        const piyue_id = await create_piyue(piyue);
        if (piyue_id) {
            await read(store.current.id, true);
            notify({
                first: "同学，您提交的练习老师批阅啦",
                hty_id: store.current_lianxi?.created_by,
                lianxi_id: piyue.lianxi_id, piyue_id,
                notify_type: NotifyTypes.PiyueCreate
            } as NotifyParam, HtyBaseRoles.STUDENT)

            let params = {
                first: `学生【${store.current_lianxi?.creator_name}】的打卡练习已被【${usingUser.store.current.real_name}】老师批阅`,
                hty_id: store.current.teacher_id,
                lianxi_id: piyue.lianxi_id, piyue_id,
                notify_type: NotifyTypes.PiyueCreate
            }
            if (usingUser.store.current.hty_id === store.current.teacher_id) {
                // send notification to all piyue teachers if supervisor piyued
                store.current.teachers?.vals?.forEach(t => {
                    notify({...params, hty_id: t.teacher_id} as NotifyParam, HtyBaseRoles.TEACHER)
                })
            } else {
                // send notification to supervisor teacher and all other piyue teachers if subsidiary teacher piyued
                notify({...params} as NotifyParam, HtyBaseRoles.TEACHER)
                store.current.teachers?.vals?.forEach(t => {
                    if (t.teacher_id === usingUser.store.current.hty_id) return;
                    notify({...params, hty_id: t.teacher_id} as NotifyParam, HtyBaseRoles.TEACHER)
                })
            }

        }
        return piyue_id;
    }

    function get_lianxi_by_id(lianxi_id: string) {
        let lianxi;
        store.current.course_sections.forEach(qs => {
            qs.lianxis?.forEach(lx => {
                if (lx.id === lianxi_id) {
                    lianxi =lx;
                    return;
                }
            })
        })
        return lianxi;
    }

    return {
        store, query, read, create, update, remove, reset, create_daka_lianxi, notify, get_shifan, prepare,
        delete_daka_lianxi, create_daka_piyue, check_tasks, create_daka_comment, load_comments, get_lianxi_by_id
    };


}
