import {reactive} from "vue";
import {
    Clazz,
    ClazzAttendance,
    ClazzRepeat,
    ClazzRepeatStatuses,
    ClazzStatuses,
    CourseSection,
    Daka,
    HtyBaseRoles,
    HtyRoles,
    HtySuperRoles,
    MultiVals,
    NotifyParam,
    NotifyTypes,
    ClazzRepeatRow,
    ReqBatchClazzAttendance,
} from "~/types";
import useUser from "~/store/user";
import useLoading from "~/store/loading";
import useComment from "~/store/comment";
import request from "~/utils/request";
import {showFailToast} from "vant";
import useNotify from "~/store/notify";
import {DateFormatter, formatDate} from "~/utils";

interface ClazzStore {
    hanging: boolean,
    current: Clazz,
    list: Clazz[],
    subsidiaryClazzByHtyId: {
        [key: string]: {list: Clazz[], repeatList: ClazzRepeatRow[]}
    }
    repeatList: ClazzRepeatRow[]
}

const store = reactive<ClazzStore>({
    hanging: false,
    current: {} as Clazz,
    list: [],
    subsidiaryClazzByHtyId: {},
    repeatList: [],
})


export default function useClazz() {

    const {load_start, load_done} = useLoading()
    const usingUser = useUser();
    const {notify_kc} = useNotify();
    const {get_resource_note_group_by_ref_id} = useComment()
    const notify = (params: NotifyParam, role_key: HtyRoles) =>  notify_kc(params, role_key);

    function normalizeClazzListPayload(payload: unknown, scene: string): Clazz[] {
        if (Array.isArray(payload)) {
            return payload as Clazz[];
        }
        const debugInfo = {
            scene,
            payload_type: Object.prototype.toString.call(payload),
            is_array: Array.isArray(payload),
            timestamp: new Date().toISOString(),
        };
        console.warn("[ClazzPayloadDebug]", debugInfo, payload);
        window.localStorage.setItem("ClazzPayloadDebug", JSON.stringify(debugInfo));
        showFailToast("排课数据格式异常，已回退为空列表");
        return [];
    }


    async function get_role_id(role_key: HtyRoles) {
        const { r, d, e } = await request({url: `/api/v1/uc/find_role_by_key/${role_key}`});
        if (r) return d.hty_role_id;
    }
    async function query(start_from: string, end_by: string) {
        let {currentRole, current:{hty_id}} = usingUser.store;
        load_start()
        const {r, d, e, statusCode} = await request({
            url: "/api/v1/clazz/find_all_non_repeatable_within_date_range_by_hty_id",
            method: "GET", data: {start_from, end_by, hty_id}
        })
        load_done()
        if (r) {
            const clazzList = normalizeClazzListPayload(d, "query_non_repeatable");
            store.list = clazzList.filter(x => {
                if (currentRole === HtyBaseRoles.TEACHER || currentRole === HtySuperRoles.SUPERVISOR) {
                    return x.teachers?.val?.users?.vals?.some(u => u.user_id === hty_id)
                }
                if (currentRole === HtyBaseRoles.STUDENT) {
                    return x.students?.val?.users?.vals?.some(u => u.user_id === hty_id)
                }
                return false;
            });
        } else {
            console.warn("[ClazzQuery] non_repeatable failed", {
                e,
                statusCode,
                start_from,
                end_by,
                hty_id,
            });
            showFailToast(e);
        }
        return Promise.resolve(r);
    }

    async function query_for_subsidiaries(start_from: string, end_by: string, hty_ids: string[]) {
        load_start()

        await Promise.all(hty_ids.map(async hty_id => {
            const {r, d, e, statusCode} = await request({
                url: "/api/v1/clazz/find_all_non_repeatable_within_date_range_by_hty_id",
                method: "GET", data: {start_from, end_by, hty_id}
            })
            if (r) {
                const clazzList = normalizeClazzListPayload(d, `query_subsidiary_non_repeatable_${hty_id}`);
                store.subsidiaryClazzByHtyId[hty_id] = {
                    list: clazzList.filter(x => x.teachers?.val?.users?.vals?.some(u => u.user_id === hty_id)),
                    repeatList: []
                }
            } else {
                console.warn("[ClazzQuery] subsidiary non_repeatable failed", {
                    e,
                    statusCode,
                    hty_id,
                    start_from,
                    end_by,
                });
            }
        }));
        load_done()
    }

    function repeat_prepare(start_from: string, end_by: string, repeatList, nonRepeatList) {
        return repeatList.map(item => {
            let row: Clazz = { clazz_repeat: {} as ClazzRepeat };
            Object.entries(item).forEach(([k, v]) => {
                if (k.startsWith("clz_")) {
                    (row as Record<string, unknown>)[k.replace("clz_", "")] = v;
                }
                if (k.startsWith("re_")) {
                    const cr = row.clazz_repeat ?? (row.clazz_repeat = {} as ClazzRepeat);
                    (cr as Record<string, unknown>)[k.replace("re_", "")] = v;
                }
            })
            if (row.is_delete) return null;
            row.repeat_start = formatDate(row.clazz_repeat?.repeat_start, DateFormatter.Date)
            row.repeat_end = formatDate(row.clazz_repeat?.repeat_end, DateFormatter.Date)
            let start = new Date(start_from).valueOf(), end = new Date(end_by).valueOf()
            let instance = nonRepeatList.find(x => x.root_id === row.id);
            let kc_start_from = new Date(row.start_from);
            let kc_end_by = new Date(row.end_by);
            if (!instance) {
                while (kc_end_by.valueOf() < start) {
                    kc_start_from.setDate(kc_start_from.getDate() + 7)
                    kc_end_by.setDate(kc_end_by.getDate() + 7)
                }
                while (kc_start_from.valueOf() > end) {
                    kc_start_from.setDate(kc_start_from.getDate() - 7)
                    kc_end_by.setDate(kc_end_by.getDate() - 7)
                }
                let {clazz_repeat, id, is_repeat, ...rest} = row
                instance = {
                    ...rest,
                    start_from: formatDate(kc_start_from, DateFormatter.DateTimeSave),
                    end_by: formatDate(kc_end_by, DateFormatter.DateTimeSave),
                }
            }
            instance!.has_root = true;
            return {...row, instance};
        }).filter(x => !!x);
    }

    async function query_repeats(start_from: string, end_by: string) {
        let {current:{hty_id}} = usingUser.store;
        load_start()
        const {r, d, e, statusCode} = await request({
            url: "/api/v1/clazz/find_all_repeatable_within_date_range_by_hty_id",
            method: "GET", data: {start_from, end_by, hty_id}
        })
        load_done()
        if (r) {
            const repeatList = normalizeClazzListPayload(d, "query_repeatable");
            store.repeatList = repeat_prepare(start_from, end_by, repeatList, store.list);
        } else {
            console.warn("[ClazzQuery] repeatable failed", {
                e,
                statusCode,
                start_from,
                end_by,
                hty_id,
            });
            showFailToast(e);
        }
        return Promise.resolve(r);
    }


    async function query_repeats_for_subsidiaries(start_from: string, end_by: string, hty_ids: string[]) {
        load_start()
        await Promise.all(hty_ids.map(async hty_id => {
            const {r, d, e, statusCode} = await request({
                url: "/api/v1/clazz/find_all_repeatable_within_date_range_by_hty_id",
                method: "GET", data: {start_from, end_by, hty_id}
            })
            if (r) {
                const repeatList = normalizeClazzListPayload(d, `query_subsidiary_repeatable_${hty_id}`);
                store.subsidiaryClazzByHtyId[hty_id] = store.subsidiaryClazzByHtyId[hty_id] || {list: [], repeatList: []}
                store.subsidiaryClazzByHtyId[hty_id].repeatList = repeat_prepare(start_from, end_by, repeatList, store.subsidiaryClazzByHtyId[hty_id].list);
            } else {
                console.warn("[ClazzQuery] subsidiary repeatable failed", {
                    e,
                    statusCode,
                    hty_id,
                    start_from,
                    end_by,
                });
            }
        }))
        load_done()
    }

    async function prepareQS(course_sections?: MultiVals<CourseSection>) {
        for (let i = 0; i < (course_sections?.vals?.length || []); i++) {
            let { resources, ref_resources, id, resource_note_group, ...rest } = course_sections?.vals[i];
            ref_resources = ref_resources || resources;
            if (!resource_note_group) {
                resource_note_group = await get_resource_note_group_by_ref_id(id)
            }
            course_sections.vals[i] = {id, ref_resources, ...rest, resource_note_group}
        }
    }

    async function createOrUpdate() {
        let { clazz_name, students, course_sections, clazz_repeat, start_from, end_by, is_repeat, instance, has_root, repeat_start, repeat_end, teachers, ...rest} = store.current;
        if (!clazz_name) {
            showFailToast("请输入排课名称！")
            return false
        }
        if (!students?.val?.users?.vals?.length) {
            showFailToast("请选择上课学生！")
            return false
        }
        let duration = (new Date(end_by).valueOf() - new Date(start_from).valueOf()) / (60 * 1000)
        if (duration <= 0) {
            showFailToast("下课时间必须晚于上课时间")
            return false
        }
        if (is_repeat) {
            if (!(repeat_start && repeat_end)) {
                showFailToast("请为重复排课选择重复时间！")
                return false;
            }
            clazz_repeat = {
                ...clazz_repeat,
                start_from, end_by, repeat_status: ClazzRepeatStatuses.OnGoing,
                repeat_start: formatDate(repeat_start, DateFormatter.DateTimeSave),
                repeat_end: formatDate(repeat_end, DateFormatter.DateTimeSave),
                repeat_cycle_days: (new Date(repeat_end).valueOf() - new Date(repeat_start).valueOf()) / (24 * 3600 * 1000)
            }
        }
        await prepareQS(course_sections)
        if (!teachers?.val?.users?.vals?.length) {
            teachers = {val: {users: {vals: []}}}
        }
        let {store:{current:{hty_id, real_name}}} = usingUser;
        if (!teachers.val.users.vals.some(u => u.user_id === hty_id)) {
            teachers.val.users.vals.push({user_id: hty_id, real_name})
        }

        // set user_type(role_key) and role_id for scheduled notification task use.
        let studentRoleId = await get_role_id(HtyBaseRoles.STUDENT), teacherRoleId = await get_role_id(HtyBaseRoles.TEACHER);
        students.val.users.vals.forEach(u => {
            u.user_type = HtyBaseRoles.STUDENT;
            u.role_id = studentRoleId
        });
        teachers.val.users.vals.forEach(u => {
            u.user_type = HtyBaseRoles.TEACHER;
            u.role_id = teacherRoleId
        });

        load_start()
        let data = {...rest, clazz_name, students, start_from, end_by, is_repeat, clazz_repeat, course_sections, duration, teachers, clazz_status: ClazzStatuses.Open};
        let res: any;
        if (rest.id) {
            res = await request({url: "/api/v1/clazz/update", method: "POST", data})
            if (res.r && is_repeat) {
                res = await request({url: "/api/v1/clazz/repeat/update", method: "POST", data: clazz_repeat})
            }
        } else {
            // set created by
            data.created_by = hty_id;
            res = await request({url: "/api/v1/clazz/create_with_repeat", method: "POST", data})
        }
        const {r, d, e} = res
        load_done()
        if (!r) {
            showFailToast(e);
        } else {
            if (!rest.id) {
                store.current = d
            }

            let teacher_name = teachers?.val?.users?.vals.map(t => t.real_name).filter(x => x).join(", ")
            let payload: NotifyParam = {
                clazz_id: store.current.id, clazz_name: store.current.clazz_name, teacher_name,
                start_from: start_from.replace("T", " ").replace(/:00$/, ""), end_by: end_by.split("T")[1].replace(/:00$/, ""),
            }
            // notify assist teacher
            let student_name = students?.val?.users?.vals.map(x => x.real_name).join(", ")
            teachers?.val?.users?.vals.forEach(t => {
                // not notify creator
                if (t.user_id !== (store.current.created_by || hty_id)) {
                    notify({ ...payload, hty_id: t.user_id, student_name, notify_type: NotifyTypes.ClazzCreateOrUpdate, } as NotifyParam, HtyBaseRoles.TEACHER)
                }
            })
            students?.val?.users?.vals.forEach(s => {
                notify({ ...payload, hty_id: s.user_id, student_name: s.real_name, notify_type: NotifyTypes.ClazzCreateOrUpdate } as NotifyParam, HtyBaseRoles.STUDENT)
            })
        }
        return r;
    }

    async function setDakasForClazz(daka: Daka) {
        try {
            let {dakas, ...rest} = store.current;
            if (dakas?.vals.length && !dakas.vals.some(x => x.id === daka.id)) {
                dakas?.vals.push(daka)
            } else {
                dakas = {vals: [daka]}
            }
            load_start()
            const { r, d, e} = await request({url: "/api/v1/clazz/update", method: "POST", data: {...rest, dakas}})
            load_done()
            if (r) {
                // reset hanging to force data refresh
                store.hanging = false;
            }
        } catch (e) {
            console.log('update clazz failed...', e)
        }

    }

    async function createInstance(instance: Clazz, is_delete?: boolean) {
        let { clazz_name, students, start_from, end_by, has_root, teachers, ...rest} = instance;
        if (!clazz_name) {
            showFailToast("请输入排课名称！")
            return false
        }
        if (!students?.val?.users?.vals?.length) {
            showFailToast("请选择上课学生！")
            return false
        }
        let duration = (new Date(end_by).valueOf() - new Date(start_from).valueOf()) / (60 * 1000)
        if (duration <= 0) {
            showFailToast("下课时间必须晚于上课时间")
            return false
        }

        if (!teachers?.val?.users?.vals?.length) {
            teachers = {val: {users: {vals: []}}}
        }
        let {store:{current:{hty_id, real_name}}} = usingUser;
        if (!teachers.val.users.vals.some(u => u.user_id === hty_id)) {
            teachers.val.users.vals.push({user_id: hty_id, real_name})
        }
        load_start()
        let data = {...rest, clazz_name, is_delete, students, start_from, end_by, duration, created_by: hty_id, teachers, clazz_status: ClazzStatuses.Open};
        const {r, d, e} = await request({url: "/api/v1/clazz/create_with_repeat", method: "POST", data})
        load_done()
        if (!r) {
            showFailToast(e);
        } else {
            if (store.current.instance) {
                store.current.instance = d;
            } else {
                store.current = d;
            }

            let teacher_name = teachers?.val?.users?.vals.map(t => t.real_name).join(", ")
            let payload: NotifyParam = {
                clazz_id: d.id, clazz_name: clazz_name, teacher_name,
                start_from: start_from.replace("T", " ").replace(/:00$/, ""), end_by: end_by.split("T")[1].replace(/:00$/, ""),
            }
            let notify_type = is_delete ? NotifyTypes.ClazzDelete : NotifyTypes.ClazzCreateOrUpdate;
            // notify assist teacher
            let student_name = students?.val?.users?.vals.map(x => x.real_name).join(", ")
            teachers?.val?.users?.vals.forEach(t => {
                // not notify creator
                if (t.user_id !== (store.current.created_by || hty_id)) {
                    notify({ ...payload, hty_id: t.user_id, student_name, notify_type, } as NotifyParam, HtyBaseRoles.TEACHER)
                }
            })
            students?.val?.users?.vals.forEach(s => {
                notify({ ...payload, hty_id: s.user_id, student_name: s.real_name, notify_type } as NotifyParam, HtyBaseRoles.STUDENT)
            })
        }
        return r;
    }

    const removeCurrent = async (is_root: boolean) => {

        if (is_root) {
            // 如果当前不是root课程，则找到对应的root课程
            let {has_root} = store.current;
            if (has_root) {
                // 实例课程，找对应的root课程
                store.current = store.repeatList.find(x => x.instance?.id === store.current.id)
            }
        } else {
            if (store.current.instance) {
                return await createInstance(store.current.instance, true)
            }
        }

        let {has_root, repeat_start, repeat_end, instance, ...rest} = store.current;
        load_start()
        const {r, d, e} = await request({
            url: "/api/v1/clazz/update", method: "POST", data: {...rest, is_delete: true}
        })
        load_done()
        if (!r) {
            showFailToast(e)
        } else {
            let { id, teachers, students, clazz_name, start_from, end_by, created_by} = store.current;
            let teacher_name = teachers?.val?.users?.vals.map(t => t.real_name).join(", ")
            let payload: NotifyParam = {
                clazz_id: id, clazz_name: clazz_name, teacher_name,
                start_from: start_from.replace("T", " ").replace(/:00$/, ""),
                end_by: end_by.split("T")[1].replace(/:00$/, ""),
            }
            // notify assist teacher
            let student_name = students?.val?.users?.vals.map(x => x.real_name).join(", ")
            teachers?.val?.users?.vals.forEach(t => {
                // not notify creator
                if (t.user_id !== created_by) {
                    notify({ ...payload, hty_id: t.user_id, student_name, notify_type: NotifyTypes.ClazzDelete, } as NotifyParam, HtyBaseRoles.TEACHER)
                }
            })
            students?.val?.users?.vals.forEach(s => {
                notify({ ...payload, hty_id: s.user_id, student_name: s.real_name, notify_type: NotifyTypes.ClazzDelete } as NotifyParam, HtyBaseRoles.STUDENT)
            })
        }
        return Promise.resolve(r)
    }

    async function find_clazz_attendance_by_clazz_id(clazz_id: string): Promise<ClazzAttendance[] | null> {
        const { r, d, e } = await request({
            url: `/api/v1/clazz/attendance/${encodeURIComponent(clazz_id)}`,
            method: "GET",
        });
        if (r) {
            return d as ClazzAttendance[];
        }
        showFailToast(e);
        return null;
    }

    async function batch_save_clazz_attendance(payload: ReqBatchClazzAttendance): Promise<boolean> {
        load_start();
        const { r, e } = await request({
            url: "/api/v1/clazz/batch_save_attendance",
            method: "POST",
            data: payload,
        });
        load_done();
        if (!r) {
            showFailToast(e);
        }
        return !!r;
    }


    async function getAuditLog(clazzId: string): Promise<any[] | null> {
        const { r, d, e } = await request({
            url: "/api/v1/clazz/audit-log/list",
            method: "GET",
            data: { clazz_id: clazzId },
        });
        if (r) return d as any[];
        showFailToast(e);
        return null;
    }

    async function queryStudentLessons(start_date: string, end_date: string): Promise<any[] | null> {
        const { r, d, e } = await request({
            url: "/api/v1/clazz/stats/my-lessons",
            method: "GET",
            data: { start_date, end_date },
        });
        if (r) return d as any[];
        showFailToast(e);
        return null;
    }

    async function queryTeacherDetailStats(start_date: string, end_date: string): Promise<any[] | null> {
        const { r, d, e } = await request({
            url: "/api/v1/clazz/stats/teacher-detail",
            method: "GET",
            data: { start_date, end_date },
        });
        if (r) return d as any[];
        showFailToast(e);
        return null;
    }

    return {
        store, query, query_repeats, createOrUpdate, removeCurrent, createInstance, notify, setDakasForClazz, query_for_subsidiaries, query_repeats_for_subsidiaries,
        find_clazz_attendance_by_clazz_id, batch_save_clazz_attendance, getAuditLog, queryStudentLessons, queryTeacherDetailStats,
    }
}
