import { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
import { oauthLoginUrl, oauthHandleRedirectIfPresent } from '@huggingface/hub';

const AuthContext = createContext(null);

const HF_API = 'https://huggingface.co';

// Whether we're running inside an iframe (i.e. embedded on huggingface.co)
const isInIframe = typeof window !== 'undefined' && window.parent !== window;

/**
 * Fetch OAuth config (clientId, scopes) from our Express server.
 * Docker Spaces don't auto-inject window.huggingface.variables like static Spaces,
 * so we expose OAUTH_CLIENT_ID via a server endpoint.
 */
async function fetchOAuthConfig() {
  try {
    const response = await fetch('/api/oauth-config');
    if (!response.ok) return null;
    return await response.json();
  } catch {
    return null;
  }
}

/**
 * Fetch all space IDs liked by a user.
 * Returns a Set of lowercase space IDs (e.g. "pollen-robotics/reachy-mini-teleop").
 */
async function fetchUserLikedSpaces(username) {
  try {
    const response = await fetch(`${HF_API}/api/users/${username}/likes`);
    if (!response.ok) return new Set();
    const likes = await response.json();
    // Filter only spaces and return their repo names as a Set
    return new Set(
      likes
        .filter((item) => item.repo?.type === 'space')
        .map((item) => item.repo.name.toLowerCase())
    );
  } catch (err) {
    console.error('[Auth] Failed to fetch user likes:', err);
    return new Set();
  }
}

/**
 * Send a like request to the HF parent frame via postMessage.
 * Returns a Promise that resolves with the response data.
 * The parent frame (huggingface.co) handles auth via session cookies.
 */
function likeViaPostMessage(spaceId) {
  return new Promise((resolve, reject) => {
    const timeout = setTimeout(() => {
      window.removeEventListener('message', handler);
      reject(new Error('Like request timed out'));
    }, 5000);

    function handler(event) {
      if (event.data?.type !== 'LIKE_REPO_RESPONSE') return;
      clearTimeout(timeout);
      window.removeEventListener('message', handler);
      resolve(event.data);
    }

    window.addEventListener('message', handler);
    window.parent.postMessage(
      { type: 'LIKE_REPO_REQUEST', repo: { type: 'space', name: spaceId } },
      '*'
    );
  });
}

// Provider component
export function AuthProvider({ children }) {
  const [user, setUser] = useState(null); // { name, avatarUrl }
  const [likedSpaceIds, setLikedSpaceIds] = useState(new Set());
  const [isLoading, setIsLoading] = useState(true);
  const [oauthConfig, setOauthConfig] = useState(null); // { clientId, scopes }
  const pendingLikes = useRef(new Set()); // Track in-flight like requests

  // On mount: fetch OAuth config + check if user just completed OAuth redirect
  useEffect(() => {
    async function init() {
      try {
        // Fetch OAuth config from server (needed for Docker Spaces)
        const config = await fetchOAuthConfig();
        if (config?.clientId) {
          setOauthConfig(config);
          console.log('[Auth] OAuth config loaded (clientId available)');
        } else {
          console.log('[Auth] OAuth not available (no clientId from server)');
        }

        // Check if user just completed OAuth redirect
        const oauthResult = await oauthHandleRedirectIfPresent();
        if (oauthResult) {
          const { userInfo } = oauthResult;
          const userData = {
            name: userInfo.name,
            preferredUsername: userInfo.preferred_username || userInfo.name,
            avatarUrl: userInfo.picture,
          };
          setUser(userData);

          // Fetch user's liked spaces
          const likes = await fetchUserLikedSpaces(userData.preferredUsername);
          setLikedSpaceIds(likes);

          console.log(
            `[Auth] Logged in as ${userData.preferredUsername}, ${likes.size} liked spaces`
          );
        }
      } catch (err) {
        console.error('[Auth] Init error:', err);
      } finally {
        setIsLoading(false);
      }
    }

    init();
  }, []);

  // Login: redirect to HF OAuth (passing clientId from server config)
  const login = useCallback(async () => {
    if (!oauthConfig?.clientId) {
      console.warn('[Auth] Cannot login: OAuth not configured');
      return;
    }

    try {
      const loginUrl = await oauthLoginUrl({
        clientId: oauthConfig.clientId,
        scopes: oauthConfig.scopes || 'openid profile',
      });
      window.location.href = loginUrl;
    } catch (err) {
      console.error('[Auth] Failed to get OAuth URL:', err);
    }
  }, [oauthConfig]);

  // Logout: clear state
  const logout = useCallback(() => {
    setUser(null);
    setLikedSpaceIds(new Set());
  }, []);

  // Check if a space is liked
  const isSpaceLiked = useCallback(
    (spaceId) => {
      return likedSpaceIds.has(spaceId?.toLowerCase());
    },
    [likedSpaceIds]
  );

  /**
   * Like a space via the HF parent frame postMessage protocol.
   * The parent (huggingface.co) handles auth via session cookies.
   * Like-only (no unlike) — if already liked, it's a no-op.
   */
  const toggleLike = useCallback(
    async (spaceId) => {
      const spaceIdLower = spaceId?.toLowerCase();
      if (!spaceIdLower) return;

      // Already liked → no-op (postMessage only supports like, not unlike)
      if (likedSpaceIds.has(spaceIdLower)) return;

      // Not in iframe → can't use postMessage, prompt OAuth login as fallback
      if (!isInIframe) {
        console.warn('[Auth] Not in iframe, postMessage unavailable');
        return;
      }

      // Prevent duplicate requests
      if (pendingLikes.current.has(spaceIdLower)) return;
      pendingLikes.current.add(spaceIdLower);

      // Optimistic update
      setLikedSpaceIds((prev) => {
        const next = new Set(prev);
        next.add(spaceIdLower);
        return next;
      });

      try {
        const result = await likeViaPostMessage(spaceId);

        if (result.error) {
          throw new Error(`${result.error.code}: ${result.error.message}`);
        }

        if (result.status === 'not_logged_in') {
          // User not logged in to HF → revert and prompt login
          throw new Error('not_logged_in');
        }

        // "done" or "already_liked" → success
        console.log(`[Auth] Liked ${spaceId}: ${result.status}`, result.likes != null ? `(${result.likes} likes)` : '');
      } catch (err) {
        console.error(`[Auth] Failed to like ${spaceId}:`, err.message);

        // Revert optimistic update
        setLikedSpaceIds((prev) => {
          const reverted = new Set(prev);
          reverted.delete(spaceIdLower);
          return reverted;
        });

        // If not logged in, prompt OAuth login
        if (err.message === 'not_logged_in') {
          login();
        }
      } finally {
        pendingLikes.current.delete(spaceIdLower);
      }
    },
    [likedSpaceIds, login]
  );

  return (
    <AuthContext.Provider
      value={{
        user,
        isLoggedIn: !!user,
        isLoading,
        isOAuthAvailable: !!oauthConfig?.clientId,
        isInIframe,
        likedSpaceIds,
        login,
        logout,
        isSpaceLiked,
        toggleLike,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

// Hook to use auth context
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
