import { createContext, useCallback, useContext, useEffect, useMemo, useState, type PropsWithChildren } from 'react';
import { authRequest, getCurrentUser } from '../api/http';
import { tokenStore, type TokenPair } from '../api/token-store';
import type { components } from '../api/schema.generated';

type AuthenticationResult = components['schemas']['AuthenticationResultDto'];
type CurrentUser = components['schemas']['MeResponse'];
const platformRealm = 1;

interface AuthContextValue {
  loading: boolean;
  user: CurrentUser | null;
  challengeToken: string | null;
  login(identifier: string, password: string): Promise<void>;
  completePasswordChange(password: string): Promise<void>;
  logout(): Promise<void>;
}

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

function tokensFrom(result: AuthenticationResult): TokenPair | null {
  const tokens = result.user?.tokens;
  return tokens?.accessToken && tokens.refreshToken ? (tokens as TokenPair) : null;
}

export function AuthProvider({ children }: PropsWithChildren) {
  const [loading, setLoading] = useState(Boolean(tokenStore.get()));
  const [user, setUser] = useState<CurrentUser | null>(null);
  const [challengeToken, setChallengeToken] = useState<string | null>(null);

  const loadUser = useCallback(async () => {
    if (!tokenStore.get()) {
      setUser(null);
      setLoading(false);
      return;
    }
    try {
      setUser(await getCurrentUser<CurrentUser>());
    } catch {
      tokenStore.set(null);
      setUser(null);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { void loadUser(); }, [loadUser]);

  const applyAuthentication = useCallback(async (result: AuthenticationResult) => {
    const tokens = tokensFrom(result);
    if (tokens) {
      tokenStore.set(tokens);
      setChallengeToken(null);
      setLoading(true);
      await loadUser();
      return;
    }
    if (result.challengeToken) {
      setChallengeToken(result.challengeToken);
      return;
    }
    throw new Error('认证响应中没有登录令牌或密码变更挑战');
  }, [loadUser]);

  const value = useMemo<AuthContextValue>(() => ({
    loading,
    user,
    challengeToken,
    async login(identifier, password) {
      const result = await authRequest<AuthenticationResult>('/api/auth/login/password', {
        realm: platformRealm,
        tenantCode: null,
        identifier,
        phone: null,
        password,
      });
      await applyAuthentication(result);
    },
    async completePasswordChange(password) {
      if (!challengeToken) throw new Error('密码变更挑战已失效，请重新登录');
      const result = await authRequest<AuthenticationResult>('/api/auth/password/change-required', {
        challengeToken,
        newPassword: password,
      });
      await applyAuthentication(result);
    },
    async logout() {
      const refreshToken = tokenStore.get()?.refreshToken;
      try {
        if (refreshToken) await authRequest('/api/auth/logout', { refreshToken });
      } finally {
        tokenStore.set(null);
        setUser(null);
        setChallengeToken(null);
      }
    },
  }), [applyAuthentication, challengeToken, loading, user]);

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

export function useAuth() {
  const value = useContext(AuthContext);
  if (!value) throw new Error('useAuth 必须在 AuthProvider 内使用');
  return value;
}
