'use client';

import { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { User, Session } from '@supabase/supabase-js';
import { getSupabase } from '@/lib/supabase';
import { useRouter } from 'next/navigation';
import { api, setApiToken } from '@/lib/api';

interface BackendUser {
  id: string;
  email: string;
  name: string | null;
  avatarUrl: string | null;
  subscriptionTier: string;
}

interface AuthContextType {
  user: User | null;
  backendUser: BackendUser | null;
  session: Session | null;
  loading: boolean;
  signUp: (email: string, password: string, name: string) => Promise<{ error: Error | null }>;
  signIn: (email: string, password: string) => Promise<{ error: Error | null }>;
  signOut: () => Promise<void>;
  getAccessToken: () => Promise<string | null>;
}

const AuthContext = createContext<AuthContextType | undefined>(undefined);

export function AuthProvider({ children }: { children: React.ReactNode }) {
  const [user, setUser] = useState<User | null>(null);
  const [backendUser, setBackendUser] = useState<BackendUser | null>(null);
  const [session, setSession] = useState<Session | null>(null);
  const [loading, setLoading] = useState(true);
  const router = useRouter();
  const supabase = getSupabase();

  useEffect(() => {
    // Get initial session
    supabase.auth.getSession().then(({ data: { session } }) => {
      setSession(session);
      setUser(session?.user ?? null);
      // Only set loading false when there's no session; otherwise wait for token exchange
      if (!session) {
        setApiToken(null);
        setLoading(false);
      }
    });

    // Listen for auth changes
    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange((_event, session) => {
      setSession(session);
      setUser(session?.user ?? null);
      if (!session) {
        setApiToken(null);
        setLoading(false);
      }
    });

    return () => subscription.unsubscribe();
  }, [supabase.auth]);

  const runExchange = useCallback(() => {
    if (!session?.access_token) {
      setApiToken(null);
      return;
    }

    let attempt = 0;
    const maxRetries = 3;

    const tryExchange = () => {
      api
        .exchangeToken(session.access_token)
        .then(({ accessToken, user: backendUserData }) => {
          setApiToken(accessToken);
          setBackendUser(backendUserData);
          setLoading(false);
        })
        .catch(() => {
          attempt++;
          if (attempt < maxRetries) {
            // Retry with exponential backoff (500ms, 1500ms, 3500ms)
            setTimeout(tryExchange, 500 * Math.pow(2, attempt));
          } else {
            setApiToken(null);
            setLoading(false);
          }
        });
    };

    tryExchange();
  }, [session?.access_token]);

  // Exchange Supabase token for backend JWT; keep loading true until done so API calls wait for JWT
  useEffect(() => {
    runExchange();
  }, [runExchange]);

  // Re-exchange on 401 (e.g. JWT expired) so next request gets a fresh token
  useEffect(() => {
    const handler = () => {
      if (session?.access_token) runExchange();
    };
    window.addEventListener('auth:unauthorized', handler);
    return () => window.removeEventListener('auth:unauthorized', handler);
  }, [session?.access_token, runExchange]);

  const signUp = useCallback(
    async (email: string, password: string, name: string) => {
      try {
        const { error } = await supabase.auth.signUp({
          email,
          password,
          options: {
            data: {
              full_name: name,
            },
          },
        });

        if (error) throw error;

        return { error: null };
      } catch (error) {
        return { error: error as Error };
      }
    },
    [supabase.auth]
  );

  const signIn = useCallback(
    async (email: string, password: string) => {
      try {
        const { error } = await supabase.auth.signInWithPassword({
          email,
          password,
        });

        if (error) throw error;

        return { error: null };
      } catch (error) {
        return { error: error as Error };
      }
    },
    [supabase.auth]
  );

  const signOut = useCallback(async () => {
    await supabase.auth.signOut();
    router.push('/');
  }, [supabase.auth, router]);

  const getAccessToken = useCallback(async () => {
    const { data } = await supabase.auth.getSession();
    return data.session?.access_token ?? null;
  }, [supabase.auth]);

  return (
    <AuthContext.Provider
      value={{
        user,
        backendUser,
        session,
        loading,
        signUp,
        signIn,
        signOut,
        getAccessToken,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  const context = useContext(AuthContext);
  if (context === undefined) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
}
