import { computed, ref } from "vue";
import { forInObject, isNull, isObject, parse, storage } from "../utils";
import { router } from "../router";
import { request } from "../service";
import type { UserInfo } from "../types";
import { type LoginData, getUsersInfo, refreshTokenApi } from "@/services/user";
import { dict } from "./dict";
export type Token = {
	token: string; // 访问token
	expire: number; // token过期时间（秒）
	refreshToken: string; // 刷新token
	refreshExpire: number; // 刷新token过期时间（秒）
};

export class User {
	/**
	 * 用户信息，响应式对象
	 */
	info = ref<UserInfo | null>(null);
	/**
	 * token刷新中的Promise，防止并发刷新
	 */
	private refreshing: Promise<string> | null = null;
	/**
	 * 用户信息，响应式对象
	 */
	courseId = ref<string | null>(null);
	/**
	 * 当前token，字符串或null
	 */
	token: string | 'Basic ZW5kOmVuZA==';

	constructor() {
		// 获取本地用户信息
		const userInfo = storage.get("userInfo");

		// 获取本地token
		const token = storage.get("token") as string | 'Basic ZW5kOmVuZA==';

		// 如果token为空字符串则置为null
		this.token = token == "" ? 'Basic ZW5kOmVuZA==' : token;
		if (this.token == 'Basic ZW5kOmVuZA==') {
			return;
		}
		// 初始化用户信息
		if (userInfo != null && isObject(userInfo)) {
			this.set(userInfo);
		} else {
			this.get()
		}
	}

	/**
	 * 获取用户信息（从服务端拉取最新信息并更新本地）
	 * @returns Promise<void>
	 */
	async get() {
		try {
			const res = await getUsersInfo()
			if (res != null) {
				this.set(res);
			}
		} catch (err) {
			console.warn("获取用户信息失败:", err);
		}
	}

	/**
	 * 设置用户信息并存储到本地
	 * @param data 用户信息对象
	 */
	set(data: any) {
		if (isNull(data)) {
			return;
		}

		// 设置
		this.info.value = parse<UserInfo>(data)!;

		// 持久化到本地存储
		storage.set("userInfo", data, 0);
	}
	setCourseId(id: string) {
		this.courseId.value = id
	}
	/**
	 * 更新用户信息（本地与服务端同步）
	 * @param data 新的用户信息
	 */
	async update(data: any) {
		if (isNull(data) || isNull(this.info.value)) {
			return;
		}

		// 本地同步更新
		forInObject(data, (value, key) => {
			this.info.value![key] = value;
		});

		// 同步到服务端
		await request({
			url: "/app/user/info/updatePerson",
			method: "POST",
			data
		});
	}

	/**
	 * 移除用户信息
	 */
	remove() {
		this.info.value = null;
		storage.remove("userInfo");
	}

	/**
	 * 判断用户信息是否为空
	 * @returns boolean
	 */
	isNull() {
		return this.info.value == null;
	}

	/**
	 * 清除本地所有用户信息和token
	 */
	clear() {
		storage.remove("userInfo");
		storage.remove("token");
		storage.remove("refreshToken");
		this.token = 'Basic ZW5kOmVuZA==';
		this.remove();
		this.info.value = null;
		this.courseId.value = null;
	}

	/**
	 * 退出登录，清除所有信息并跳转到登录页
	 */
	logout() {
		this.clear();
		router.login();
	}

	/**
	 * 设置token并存储到本地
	 * @param data Token对象
	 */
	setToken(data: LoginData) {
		this.token = `Bearer ${data?.access_token}`;

		// 访问token，提前5秒过期，防止边界问题
		storage.set("token", this.token, 7 * 24 * 60 * 60 - 5);
		this.get();
		dict.refresh();
		
		// 存储refresh_token（服务端默认30天有效期）
		if (data != null && data.refresh_token != null) {
			storage.set("refreshToken", data.refresh_token, 30 * 24 * 60 * 60 - 5);
		}

	}


	/**
	 * 刷新token（调用服务端OAuth2接口，自动更新本地token）
	 * @returns Promise<string> 新的token
	 */
	async refreshToken(): Promise<string> {
		// 防止并发刷新
		if (this.refreshing != null) {
			return this.refreshing;
		}

		const storedRefreshToken = storage.get("refreshToken") as string;
		if (storedRefreshToken == '' || storedRefreshToken.length == 0) {
			return Promise.reject(new Error("No refresh token available"));
		}

		this.refreshing = (async () => {
			try {
				const res = await refreshTokenApi(storedRefreshToken);
				if (res != null && res.data != null) {
					const respData = res.data as UTSJSONObject;
					const loginData: LoginData = {
						access_token: respData["access_token"] as string,
						refresh_token: respData["refresh_token"] as string,
					};
					this.setToken(loginData);
					return this.token;
				}
				throw new Error("Refresh token response invalid");
			} finally {
				this.refreshing = null;
			}
		})();

		return this.refreshing;
	}
}

/**
 * 单例用户对象，项目全局唯一
 */
export const user = new User();

/**
 * 用户信息，响应式对象
 */
export const userInfo = computed(() => user.info.value);
