// ============================================================
// Auth Context — 认证状态管理
// 职责：登录/注册/登出/token 刷新，为子组件提供 auth 状态
// ============================================================

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
  type ReactNode,
} from "react";
import * as api from "./api";
import type { AuthUser } from "./api";
import { setAuthCallbacks } from "./api";
import {
  clearAuth,
  loadAccessToken,
  loadRefreshToken,
  loadUser,
  saveAccessToken,
  saveRefreshToken,
  saveUser,
} from "./storage";

interface AuthState {
  user: AuthUser | null;
  isAuthenticated: boolean;
  isLoading: boolean;
}

interface AuthContextValue extends AuthState {
  login: (username: string, password: string) => Promise<{ error?: string }>;
  register: (username: string, password: string) => Promise<{ error?: string }>;
  logout: () => Promise<void>;
  accessToken: string | null;
}

const AuthContext = createContext<AuthContextValue | null>(null);

/** Access token 提前刷新的缓冲时间（秒） */
const REFRESH_BUFFER_SEC = 60;

/** 解析 JWT payload（不做签名验证） */
function parseJwtPayload(token: string): { exp?: number } | null {
  try {
    const base64 = token.split(".")[1];
    const json = atob(base64.replace(/-/g, "+").replace(/_/g, "/"));
    return JSON.parse(json);
  } catch {
    return null;
  }
}

export function AuthProvider({ children }: { children: ReactNode }) {
  const [user, setUser] = useState<AuthUser | null>(loadUser);
  const [accessToken, setAccessToken] = useState<string | null>(loadAccessToken);
  const [isLoading, setIsLoading] = useState(true);
  const refreshTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // 清除定时器
  const clearRefreshTimer = useCallback(() => {
    if (refreshTimerRef.current) {
      clearTimeout(refreshTimerRef.current);
      refreshTimerRef.current = null;
    }
  }, []);

  // 持久化 token + user
  const persistAuth = useCallback(
    (authUser: AuthUser, access: string, refresh: string) => {
      setUser(authUser);
      setAccessToken(access);
      saveAccessToken(access);
      saveRefreshToken(refresh);
      saveUser(authUser);
    },
    []
  );

  // 安排自动刷新
  const scheduleRefresh = useCallback(
    (access: string) => {
      clearRefreshTimer();
      const payload = parseJwtPayload(access);
      if (!payload?.exp) return;

      const nowSec = Math.floor(Date.now() / 1000);
      const delayMs = Math.max((payload.exp - nowSec - REFRESH_BUFFER_SEC) * 1000, 5000);

      refreshTimerRef.current = setTimeout(async () => {
        const rt = loadRefreshToken();
        if (!rt) return;
        const res = await api.refreshToken(rt);
        if (res.data) {
          persistAuth(res.data.user, res.data.access_token, res.data.refresh_token);
          scheduleRefresh(res.data.access_token);
        } else {
          // 刷新失败，清除 auth
          clearAuth();
          setUser(null);
          setAccessToken(null);
        }
      }, delayMs);
    },
    [clearRefreshTimer, persistAuth]
  );

  // 注册 API 层认证回调（用于 401 拦截器）
  useEffect(() => {
    setAuthCallbacks({
      getAccessToken: () => loadAccessToken(),
      getRefreshToken: () => loadRefreshToken(),
      onRefreshSuccess: (u, at, rt) => {
        persistAuth(u, at, rt);
        scheduleRefresh(at);
      },
      onRefreshFailed: () => {
        clearAuth();
        setUser(null);
        setAccessToken(null);
      },
    });
  }, [persistAuth, scheduleRefresh]);

  // 初始化：检查已有 token 并尝试刷新
  useEffect(() => {
    const init = async () => {
      const storedAccess = loadAccessToken();
      const storedRefresh = loadRefreshToken();
      const storedUser = loadUser();

      if (!storedAccess || !storedRefresh || !storedUser) {
        setIsLoading(false);
        return;
      }

      // 检查 access token 是否过期
      const payload = parseJwtPayload(storedAccess);
      const nowSec = Math.floor(Date.now() / 1000);

      if (payload?.exp && payload.exp > nowSec) {
        // access token 仍然有效
        setUser(storedUser);
        setAccessToken(storedAccess);
        scheduleRefresh(storedAccess);
      } else {
        // access token 过期，尝试 refresh
        const res = await api.refreshToken(storedRefresh);
        if (res.data) {
          persistAuth(res.data.user, res.data.access_token, res.data.refresh_token);
          scheduleRefresh(res.data.access_token);
        } else {
          clearAuth();
        }
      }
      setIsLoading(false);
    };

    init();
    return () => clearRefreshTimer();
  }, []); // eslint-disable-line react-hooks/exhaustive-deps

  const login = useCallback(
    async (username: string, password: string): Promise<{ error?: string }> => {
      const res = await api.login(username, password);
      if (res.data) {
        persistAuth(res.data.user, res.data.access_token, res.data.refresh_token);
        scheduleRefresh(res.data.access_token);
        return {};
      }
      return { error: res.error?.message || "登录失败" };
    },
    [persistAuth, scheduleRefresh]
  );

  const register = useCallback(
    async (username: string, password: string): Promise<{ error?: string }> => {
      const res = await api.register(username, password);
      if (res.data) {
        persistAuth(res.data.user, res.data.access_token, res.data.refresh_token);
        scheduleRefresh(res.data.access_token);
        return {};
      }
      return { error: res.error?.message || "注册失败" };
    },
    [persistAuth, scheduleRefresh]
  );

  const logout = useCallback(async () => {
    const at = accessToken;
    const rt = loadRefreshToken();
    if (at && rt) {
      await api.logout(at, rt);
    }
    clearRefreshTimer();
    clearAuth();
    setUser(null);
    setAccessToken(null);
  }, [accessToken, clearRefreshTimer]);

  const value = useMemo<AuthContextValue>(
    () => ({
      user,
      isAuthenticated: !!user && !!accessToken,
      isLoading,
      login,
      register,
      logout,
      accessToken,
    }),
    [user, accessToken, isLoading, login, register, logout]
  );

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) {
    throw new Error("useAuth must be used within an AuthProvider");
  }
  return ctx;
}
