import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import SplashPage from '../pages/SplashPage';
import type { User } from '@supabase/supabase-js';
import { supabase } from '../supabase';
import {
  buildCacheKey,
  readCache,
  writeCache,
  clearUserCache,
} from '../utils/localCache';

export interface Profile {
  id: string;
  first_name: string;
  last_name: string;
  full_name: string;
  phone: string;
  country_of_residence: string;
  avatar_path: string | null;
  fcm_token: string | null;
  notification_push_enabled: boolean;
  notification_email_enabled: boolean;
  notification_sms_enabled: boolean;
  created_at: string;
  updated_at: string;
}

export type ProfileStatus = 'idle' | 'loading' | 'loaded' | 'missing' | 'error';

interface AuthContextType {
  user: User | null;
  profile: Profile | null;
  profileStatus: ProfileStatus;
  refreshProfile: () => Promise<Profile | null>;
  setProfile: (profile: Profile | null) => void;
  signOut: () => Promise<void>;
}

const BYPASS_AUTH = false;

const AuthContext = createContext<AuthContextType>({
  user: null,
  profile: null,
  profileStatus: BYPASS_AUTH ? 'idle' : 'loading',
  refreshProfile: async () => null,
  setProfile: () => {},
  signOut: async () => {},
});

const withTimeout = async <T,>(
  request: PromiseLike<T>,
  timeoutMs: number,
  label: string
): Promise<T> => {
  let timeoutId: ReturnType<typeof setTimeout> | undefined;

  const timeout = new Promise<never>((_, reject) => {
    timeoutId = setTimeout(() => {
      reject(
        new Error(
          `${label} timed out. Please check your connection and try again.`
        )
      );
    }, timeoutMs);
  });

  try {
    return await Promise.race([Promise.resolve(request), timeout]);
  } finally {
    if (timeoutId) clearTimeout(timeoutId);
  }
};

export const useAuth = () => useContext(AuthContext);

export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
  children,
}) => {
  const [user, setUser] = useState<User | null>(null);
  const [profile, setProfileState] = useState<Profile | null>(null);
  const [profileStatus, setProfileStatus] = useState<ProfileStatus>(
    BYPASS_AUTH ? 'idle' : 'loading'
  );
  const [initializing, setInitializing] = useState(!BYPASS_AUTH);
  const activeProfileFetchRef = useRef(0);
  const mountedRef = useRef(false);
  const currentUserIdRef = useRef<string | null>(null);

  const applyProfile = useCallback((nextProfile: Profile | null) => {
    if (!mountedRef.current) return;
    setProfileState(nextProfile);
    setProfileStatus(nextProfile ? 'loaded' : 'missing');
  }, []);

  const setProfile = useCallback(
    (nextProfile: Profile | null) => {
      activeProfileFetchRef.current += 1;
      applyProfile(nextProfile);
    },
    [applyProfile]
  );

  const fetchProfile = useCallback(
    async (
      userId: string,
      options?: { showLoading?: boolean }
    ): Promise<Profile | null> => {
      const fetchId = activeProfileFetchRef.current + 1;
      activeProfileFetchRef.current = fetchId;

      if ((options?.showLoading ?? true) && mountedRef.current) {
        setProfileStatus('loading');
      }

      const cacheKey = buildCacheKey(userId, 'profile');
      try {
        const cachedProfile = await readCache<Profile>(cacheKey);
        if (
          cachedProfile &&
          mountedRef.current &&
          fetchId === activeProfileFetchRef.current &&
          currentUserIdRef.current === userId
        ) {
          applyProfile(cachedProfile);
        }
      } catch (err) {
        console.error('[AuthProvider] Failed to read cached profile', err);
      }

      try {
        const { data, error } = await withTimeout(
          supabase.from('profiles').select('*').eq('id', userId).maybeSingle(),
          8000,
          'Profile loading'
        );

        if (
          !mountedRef.current ||
          fetchId !== activeProfileFetchRef.current ||
          currentUserIdRef.current !== userId
        ) {
          return data ? (data as Profile) : null;
        }

        if (error) {
          console.error('[AuthProvider] Failed to fetch profile', error);
          setProfileState(null);
          setProfileStatus('error');
          return null;
        }

        const nextProfile = data ? (data as Profile) : null;
        applyProfile(nextProfile);
        if (nextProfile) {
          void writeCache(cacheKey, nextProfile);
        }
        return nextProfile;
      } catch (error) {
        if (
          mountedRef.current &&
          fetchId === activeProfileFetchRef.current &&
          currentUserIdRef.current === userId
        ) {
          console.error('[AuthProvider] Profile fetch crashed', error);
          setProfileState(null);
          setProfileStatus('error');
        }
        return null;
      }
    },
    [applyProfile]
  );

  useEffect(() => {
    mountedRef.current = true;

    if (BYPASS_AUTH) {
      setInitializing(false);
      setProfileStatus('idle');
      return () => {
        mountedRef.current = false;
        activeProfileFetchRef.current += 1;
      };
    }

    let cancelled = false;

    supabase.auth
      .getSession()
      .then(async ({ data }) => {
        if (cancelled || !mountedRef.current) return;
        const currentUser = data.session?.user ?? null;

        const keepSignedIn = localStorage.getItem('kumusha_keep_signed_in');
        if (currentUser && keepSignedIn === 'false') {
          currentUserIdRef.current = null;
          setUser(null);
          setProfileState(null);
          setProfileStatus('idle');
          await clearUserCache(currentUser.id);
          await supabase.auth.signOut();
          if (!cancelled && mountedRef.current) {
            setInitializing(false);
          }
          return;
        }

        currentUserIdRef.current = currentUser?.id ?? null;
        setUser(currentUser);
        if (currentUser) {
          await fetchProfile(currentUser.id, { showLoading: false });
        } else {
          setProfileState(null);
          setProfileStatus('idle');
        }
      })
      .catch((error) => {
        console.error('[AuthProvider] Failed to initialise session', error);
        if (!cancelled && mountedRef.current) {
          currentUserIdRef.current = null;
          setUser(null);
          setProfileState(null);
          setProfileStatus('missing');
        }
      })
      .finally(() => {
        if (!cancelled && mountedRef.current) {
          setInitializing(false);
        }
      });

    const {
      data: { subscription },
    } = supabase.auth.onAuthStateChange(async (_event, session) => {
      const nextUser = session?.user ?? null;
      if (!mountedRef.current) return;
      const previousUserId = currentUserIdRef.current;
      currentUserIdRef.current = nextUser?.id ?? null;

      if (nextUser) {
        if (nextUser.id !== previousUserId) {
          setProfileState(null);
          setProfileStatus('loading');
        }
        setUser(nextUser);
        await fetchProfile(nextUser.id, { showLoading: false });
      } else {
        activeProfileFetchRef.current += 1;
        setUser(null);
        setProfileState(null);
        setProfileStatus('idle');
        if (previousUserId) {
          await clearUserCache(previousUserId);
        }
      }
    });

    return () => {
      cancelled = true;
      mountedRef.current = false;
      activeProfileFetchRef.current += 1;
      subscription.unsubscribe();
    };
  }, [fetchProfile]);

  const signOut = useCallback(async () => {
    activeProfileFetchRef.current += 1;
    const currentUserId = currentUserIdRef.current;
    currentUserIdRef.current = null;
    setUser(null);
    setProfileState(null);
    setProfileStatus('idle');
    if (currentUserId) {
      await clearUserCache(currentUserId);
    }
    localStorage.removeItem('kumusha_keep_signed_in');
    await supabase.auth.signOut();
  }, []);

  const refreshProfile = useCallback(() => {
    return user
      ? fetchProfile(user.id, { showLoading: false })
      : Promise.resolve(null);
  }, [fetchProfile, user]);

  const contextValue = useMemo(
    () => ({
      user,
      profile,
      profileStatus,
      refreshProfile,
      setProfile,
      signOut,
    }),
    [profile, profileStatus, refreshProfile, setProfile, signOut, user]
  );

  if (initializing) {
    return <SplashPage title="Send support to loved ones from anywhere" />;
  }

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