import {reactive, ref} from 'vue';
import request from "../utils/request";
import {showDialog, showFailToast} from "vant";
import {CurrentUserName, CurrentUserRole, HtyAuthToken, HtySudoToken, HtyUnionIDToken} from '~/utils';
import cookie from '../utils/cookie';
import {
    HtyApplyUser, HtyBaseRoles, HtyRole, HtyRoles, HtyUser, HtyUserApp,
    NotifyParam, NotifyTypes, TeacherStudentStates, User, TongzhiQueryParam,
    TongzhiStatuses, WxScenes, UserRelTypes, UserRel
} from "~/types";
import {Router} from "vue-router";
import useLoading from "~/store/loading";
import useNotify from "~/store/notify";
import usePool from "~/store/pool";
import useTeacher from "~/store/teacher";

interface UserState {
    current: User,
    unionid: string,
    students: {
        [TeacherStudentStates.Approved]: User[],
        [TeacherStudentStates.Waiting]: User[],
        [TeacherStudentStates.Rejected]: User[],
        [TeacherStudentStates.Removed]: User[]
    },
    teachers: {
        all: User[],
        mine: User[],
        valid: User[]
    }
    subsidiaries: UserRel[],
    unclaimed: User[],
    currentRole?: HtyRoles,
    roles: HtyRole[],
    userMeta: HtyUser,
    userAppMeta: HtyUserApp,
    editing: boolean,
    wxPayload: {openid?: string, nickName?: string, avatarUrl?: string, scene?: string, status?: string}
}

const store = reactive<UserState>({
    current: {} as User,
    unionid: '',
    students: {
        [TeacherStudentStates.Approved]: [],
        [TeacherStudentStates.Waiting]: [],
        [TeacherStudentStates.Rejected]: [],
        [TeacherStudentStates.Removed]: []
    },
    teachers: {
        all: [], mine: [], valid: []
    },
    subsidiaries: [],
    unclaimed: [],
    roles: [],
    userMeta: {} as HtyUser,
    userAppMeta: {} as HtyUserApp,
    editing: false,
    wxPayload: {}
});

const teachers_loading = ref(false)

function parseCurrentOrgIdFromToken(token: string | null): string | undefined {
    if (!token) return undefined;
    try {
        const tokenParts = token.split('.');
        if (tokenParts.length < 2) return undefined;
        const payloadRaw = tokenParts[1].replace(/-/g, '+').replace(/_/g, '/');
        const payloadRawWithPadding = payloadRaw.padEnd(Math.ceil(payloadRaw.length / 4) * 4, '=');
        const payloadJson = decodeURIComponent(
            atob(payloadRawWithPadding)
                .split('')
                .map((char) => `%${(`00${char.charCodeAt(0).toString(16)}`).slice(-2)}`)
                .join('')
        );
        const payload = JSON.parse(payloadJson) as Record<string, unknown>;
        if (typeof payload.current_org_id === 'string' && payload.current_org_id) {
            return payload.current_org_id;
        }
        if (typeof payload.sub === 'string' && payload.sub.length > 0) {
            const subjectPayload = JSON.parse(payload.sub) as Record<string, unknown>;
            if (typeof subjectPayload.current_org_id === 'string' && subjectPayload.current_org_id) {
                return subjectPayload.current_org_id;
            }
        }
        return undefined;
    } catch (_error) {
        return undefined;
    }
}

export default function useUser(router?: Router) {

    const { load_start, load_done } = useLoading();
    const {notify_uc} = useNotify();
    const {setKey} = usePool()

    function setCurrentUser(user: HtyUser | {}, userApp: HtyUserApp | {}) {
        store.current = {...user as HtyUser, ...userApp as HtyUserApp};
        store.userMeta = user as HtyUser;
        store.userAppMeta = userApp as HtyUserApp
        let { real_name } = user as HtyUser;
        if (real_name) {
            cookie.set(CurrentUserName, real_name);
        } else {
            cookie.remove(CurrentUserName);
        }
    }

    function loadMyTeachers() {
        if (!store.teachers.mine.length && !teachers_loading.value) {
            getMyTeachers();
        }
    }

    async function getStudents(status: TeacherStudentStates) {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/ws/find_all_students_by_teacher_id_and_status',
            method: 'POST', data: {teacher_id: store.current.hty_id, status}
        });
        load_done()
        if (r) {
            // @ts-ignore
            store.students[status] = d.map(({infos, ...rest}: HtyUser) => ({...rest, ...(infos[0])}));
        } else {
            store.students[status] = [];
            showFailToast(e);
        }
    }

    async function getMyTeachers() {
        load_start()
        teachers_loading.value = true;
        const {r, d, e} = await request({url: `/api/v1/ws/find_all_teachers_by_student_id/${store.current.hty_id}`});
        load_done()
        if (r) {
            // @ts-ignore
            store.teachers.mine = d.map(({infos, ...rest}: HtyUser) => ({...rest, ...(infos[0])}));
        } else {
            store.teachers.mine = [];
            showFailToast(e);
        }
        teachers_loading.value = false
    }

    async function getAllTeachers() {
        load_start()
        const {r, d, e} = await request({url: `/api/v1/uc/find_users_with_info_by_role/${HtyBaseRoles.TEACHER}`});
        load_done()
        if (r) {
            // @ts-ignore
            store.teachers.all = d.map(({infos, ...rest}: HtyUser) => ({...rest, ...(infos[0])}));
        } else {
            store.teachers.all = [];
            showFailToast(e);
        }
    }

    async function approveTeacher(teacher_id: string) {
        let verified = await verify(teacher_id, true);
        if (!verified) {
            showFailToast('操作失败！请稍后再试')
            return Promise.resolve(false);
        }
        await getAllTeachers();
        // send notify
        notify_uc({ hty_id: teacher_id, notify_type: NotifyTypes.TeacherRegisterApproved } as NotifyParam, HtyBaseRoles.TEACHER)
    }

    async function rejectTeacher(teacher_id: string, reject_reason: string) {
        let verified = await verify(teacher_id, false, reject_reason);
        if (!verified) {
            showFailToast('操作失败！请稍后再试')
            return Promise.resolve(false);
        }
        await getAllTeachers();
        // send notify
        notify_uc({ hty_id: teacher_id, reject_reason, notify_type: NotifyTypes.RejectRegister } as NotifyParam, HtyBaseRoles.TEACHER)
    }

    async function getValidTeachers() {
        load_start()
        const {r, d, e} = await request({url: `/api/v1/uc/find_all_valid_teachers`});
        load_done()
        if (r) {
            store.teachers.valid = d;
        } else {
            store.teachers.valid = [];
            showFailToast(e);
        }
    }


    async function removeStudent(student_id: string) {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/ws/update_teacher_student', method: 'POST',
            data: {student_id, teacher_id: store.current.hty_id, status: TeacherStudentStates.Removed}
        });
        load_done()
        if (r) {
            await getStudents(TeacherStudentStates.Approved);
            await getStudents(TeacherStudentStates.Removed);
        } else {
            showFailToast(e);
        }
    }

    async function restoreStudent(student_id: string) {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/ws/update_teacher_student', method: 'POST',
            data: {student_id, teacher_id: store.current.hty_id, status: TeacherStudentStates.Approved}
        });
        load_done()
        if (r) {
            await getStudents(TeacherStudentStates.Approved);
            await getStudents(TeacherStudentStates.Removed);
        } else {
            showFailToast(e);
        }
    }

    async function getAppByDomain() {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/uc/find_app_by_domain'
        });
        load_done()
        if (r) {
            return Promise.resolve(d)
        } else {
            return Promise.resolve()
        }

    }

    async function verify(student_id: string, validate: boolean, reject_reason?: string) {
        let app = await getAppByDomain();
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/uc/register/verify', method: 'POST',
            data: {
                hty_id: student_id, app_id: app.app_id, validate, reject_reason
            }
        });
        load_done()
        return Promise.resolve(r)
    }

    async function approveStudent(student_id: string) {
        let verified = await verify(student_id, true);
        if (!verified) {
            showFailToast('操作失败！请稍后再试')
            return Promise.resolve(false);
        }
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/ws/update_teacher_student', method: 'POST',
            data: {student_id, teacher_id: store.current.hty_id, status: TeacherStudentStates.Approved}
        });
        load_done()
        if (r) {
            await getStudents(TeacherStudentStates.Approved);
            await getStudents(TeacherStudentStates.Waiting);
            // send notify
            notify_uc({ hty_id: student_id, hty_id2: store.current.hty_id, notify_type: NotifyTypes.StudentRegisterApproved } as NotifyParam, HtyBaseRoles.STUDENT)
        } else {
            showFailToast(e);
        }
    }

    async function rejectStudent(student_id: string, reject_reason: string) {
        let verified = await verify(student_id, false, reject_reason);
        if (!verified) {
            showFailToast('操作失败！请稍后再试')
            return Promise.resolve(false);
        }
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/ws/update_teacher_student', method: 'POST',
            data: {student_id, teacher_id: store.current.hty_id, status: TeacherStudentStates.Rejected}
        });
        load_done()
        if (r) {
            await getStudents(TeacherStudentStates.Waiting);
            await getStudents(TeacherStudentStates.Rejected);
            // send notify
            notify_uc({ hty_id: student_id, hty_id2: store.current.hty_id, reject_reason, notify_type: NotifyTypes.RejectRegister } as NotifyParam, HtyBaseRoles.STUDENT)
        } else {
            showFailToast(e);
        }
    }

    async function claimStudent(student_id: string) {
        load_start()
        const {r, d, e} = await request({
            url: `/api/v1/ws/claim_student?student_id=${student_id}&teacher_id=${store.current.hty_id}`, method: 'POST'
        });
        load_done()
        if (r) {
            await getStudents(TeacherStudentStates.Approved);
        } else {
            showFailToast(e);
        }
    }

    async function claimTeacher(student_id: string, teacher_id: string) {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/ws/link_teacher_student',
            method: 'POST', data: {teacher_id, student_id}
        });
        load_done()
        if (r) {
            return Promise.resolve();
        } else {
            console.error('register error...', e);
            return Promise.reject();
        }
    }

    async function rollback(ids: string[]) {
        load_start();
        const {r, d, e} = await request({
            url: '/api/v1/uc/register/rollback',
            method: 'POST', data: ids
        });
        load_done()
        if (r) {
            return Promise.resolve();
        } else {
            console.error('register error...', e);
            return Promise.reject();
        }
    }

    async function disclaimStudent(studentId: string) {
        load_start()
        const {r, d, e} = await request({
            url: `/api/v1/ws/disclaim_student?student_id=${studentId}&teacher_id=${store.current.hty_id}`,
            method: "POST"
        });
        load_done()
        if (r) {
            await getStudents(TeacherStudentStates.Approved);
        } else {
            showFailToast(e);
        }
    }

    async function getUnclaimedStudents() {
        load_start()
        const {r, d, e} = await request({url: '/api/v1/ws/get_unclaimed_students'});
        load_done()
        if (r) {
            // @ts-ignore
            store.unclaimed = d.map(({infos, ...rest}: HtyUser) => ({...rest, ...(infos[0])}));
        } else {
            store.unclaimed = [];
            showFailToast(e);
        }
    }

    async function getSupervisor() {
        load_start()
        const {r, d, e} = await request({
            url: `/api/v1/uc/find_all_user_rels_by_params`, data: {to_user_id: store.current.hty_id, rel_type: UserRelTypes.TeacherToTeacher}
        });
        load_done()
        if (r) {
            if (d && d[0]) {
                let { from_user_id, from_user_realname } = d[0];
                store.current.supervisor = {
                    teacher_id: from_user_id,
                    teacher_name: from_user_realname
                }
            }
        } else {
            showFailToast(e);
        }
    }



    async function read() {
        load_start()
        const {r, d, e} = await request({url: '/api/v1/uc/find_user_with_info_by_token'});
        load_done()
        if (r) {
            let user = d as HtyUser;
            // @ts-ignore
            let userApp = user.infos[0] as HtyUserApp;
            setCurrentUser(user, userApp);
            const authToken = window.localStorage.getItem(HtyAuthToken);
            const currentOrgId = parseCurrentOrgIdFromToken(authToken);
            if (currentOrgId) {
                window.localStorage.setItem(HtySudoToken, authToken as string);
            } else {
                await sudo2(userApp.id);
            }
            await getTags();
            await getMyTeachers();
            await getSupervisor();
            // check if user need to reload page, for some changes may have issue with cache
            if (userApp.needs_refresh) {
                showDialog({
                    title: '检测到系统更新',
                    allowHtml: true,
                    messageAlign: 'left',
                    message: `
                        <div>本次更新需要重新加载页面，<strong class="text-danger">『请点击小程序右上角 <sup><span style="display:inline-flex;align-items:center;"><i class="fa-solid fa-circle" style="font-size:0.25em;"></i><i class="fa-solid fa-circle" style="font-size:0.375em;margin:0 1px;"></i><i class="fa-solid fa-circle" style="font-size:0.25em"></i></span></sup> ，然后点击重新进入小程序。』</strong></div>
                        <div class="text-primary">如您已重新进入小程序，请点击确认。</div>
                    `
                }).then(async () => {
                    await update(user, {...userApp, needs_refresh: false});
                    // for case that user not reload manually
                    window.location.reload();
                })
            }
        } else {
            setCurrentUser({}, {});
            window.localStorage.removeItem(HtyAuthToken);
            window.localStorage.removeItem(HtySudoToken);
            window.location.reload();
            showFailToast(e)
        }
    }

    async function getTags() {
        load_start()
        const {r, d, e} = await request({url: '/api/v1/uc/get_all_tags_of_the_user'});
        load_done()
        if (r) {
            store.current.tags = d;
        } else {
            showFailToast(e)
        }
    }

    async function getHtyUser(hty_id: string) {
        load_start();
        const { r, d, e } = await request({
          url: "/api/v1/uc/find_hty_user_by_id/" + hty_id,
        });
        load_done();
        if (r) {
          return d;
        } else {
          showFailToast(e);
        }
    }

    async function getUserByUnionid(unionid: string) {
        load_start()
        const { r, d, e } = await request({
          url: "/api/v1/uc/find_user_by_unionid_and_domain?unionid=" + unionid,
        });
        load_done()
        if (r) {
          return d
        } else {
          showFailToast(e)
        }
    }

    async function login(unionid: string) {
        store.unionid = unionid;
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/uc/login2_with_unionid', headers: {[HtyUnionIDToken]: unionid}
        });
        load_done()
        if (r) {
            // we added roles to token, that leads the token very large (more than 4096b which is over the cookie's limit)
            // so we need to store the token to storage.
            window.localStorage.setItem(HtyAuthToken, d)
            await read();
        } else {
            window.localStorage.removeItem(HtyAuthToken)
            cookie.remove(HtyUnionIDToken);
        }
    }

    async function sudo() {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/uc/sudo', method: 'POST'
        });
        load_done()
        if (r) {
            window.localStorage.setItem(HtySudoToken, d);
            return await read();
        } else {
            window.localStorage.removeItem(HtySudoToken)
            showFailToast(e);
            return Promise.reject();
        }
    }

    async function sudo2(user_info_id?: string) {
        load_start()
        const {r, d, e} = await request({
            url: '/api/v1/uc/sudo2/' + (user_info_id || store.current.id)
        });
        load_done()
        if (r) {
            window.localStorage.setItem(HtySudoToken, d);
        } else {
            window.localStorage.removeItem(HtySudoToken)
            showFailToast(e);
            return Promise.reject();
        }
    }


    async function setRole(role: HtyRole) {
        load_start()
        const {r, e} = await request({
            url: '/api/v1/uc/create_or_update_userinfo_with_roles',
            method: 'POST',
            data: {...store.current, roles: [role]}
        });
        load_done()
        if (r) {
            await read();
            store.currentRole = role.role_key;
        } else {
            showFailToast(e)
        }
    }

    async function update(user: HtyUser, userApp: HtyUserApp){
        load_start()
        const { r, e } = await request({
            url: '/api/v1/uc/create_or_update_user_with_info',
            method: 'POST',
            data: [user, userApp]
        });
        load_done()
        if(r) {
            await read();
            return Promise.resolve();
        } else {
            showFailToast(e);
            console.log(e)
            return Promise.reject();
        }
    }

    async function register(user: HtyApplyUser): Promise<HtyUser>{
        load_start()
        const { r, d, e } = await request({
            url: '/api/v1/uc/register',
            method: 'POST', data: user
        });
        load_done()
        if(r) {
            return d;
        } else {
            showFailToast("注册失败！请联系管理员");
            console.error('register error...', e);
            return Promise.reject();
        }
    }

    function chooseRole(role?: HtyRoles, scene?: WxScenes) {
        if (!role) return;
        let prev = window.localStorage.getItem(CurrentUserRole);
        store.currentRole = role;
        window.localStorage.setItem(CurrentUserRole, role)
        setKey("current_role", role);
        getUnreadTongzhis();

        if (prev !== role && !window.location.pathname.split('/').includes(role.toLowerCase())) {
            let len = window.history.length;
            // clear session history to prevent user go back to pages of prev role
            router?.go(-len);
            if (scene === WxScenes.TemplateMessage) {
                router?.push("/tongzhi")
            } else if (role === HtyBaseRoles.GUEST) {
                router?.push('/guest')
            } else if (role === HtyBaseRoles.STUDENT) {
                // Clear teacher; index page will auto-select or redirect
                const { clearTeacher } = useTeacher();
                clearTeacher();
                router?.push('/');
            } else {
                // go to profile page
                router?.push('/' + role.toLowerCase() + '/profile')
            }
        }
    }

    async function getUnreadTongzhis() {
        try {
            let role = store.currentRole;
            let role_id = store.current.roles.find(r => r.role_key === role)?.hty_role_id;
            let params: TongzhiQueryParam = {
                role_id, tongzhi_status: TongzhiStatuses.Unread,
                user_id: store.current.hty_id
            };
            const {r, d, e} = await request({url: '/api/v1/uc/count_unread_tongzhis_by_user_id_and_role_id', data: params});
            if (r) {
                store.current.unread_tongzhi_count = d;
            } else {
                store.current.unread_tongzhi_count = 0;
            }
        } catch(e) {
            console.error("get unread tongzhi failed...", e);
        }
    }

    const set_editing = (editing: boolean) => {
        store.editing = editing;
    }

    const logout = () => {
        store.current = {} as User;
        window.localStorage.removeItem(HtyAuthToken);
        window.localStorage.removeItem(HtySudoToken);
        window.localStorage.removeItem(CurrentUserRole)
    }

    return {
      store,
      getStudents,
      getMyTeachers,
      getAllTeachers,
      getValidTeachers,
      getUnclaimedStudents,
      claimStudent,
      disclaimStudent,
      read,
      update,
      login,
      sudo,
      setRole,
      sudo2,
      getHtyUser,
      chooseRole,
      register,
      claimTeacher,
      rollback,
      removeStudent,
      rejectStudent,
      approveStudent,
      restoreStudent,
      approveTeacher,
      rejectTeacher,
      getUnreadTongzhis,
      set_editing,
      loadMyTeachers,
      logout,
      getUserByUnionid,
    };
}
