import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import type { User } from '@supabase/supabase-js';
import { supabase } from '../supabase';

export interface Profile {
  id: string;
  first_name: string;
  last_name: string;
  full_name: string;
  phone: string;
  country_of_residence: string;
  avatar_path: 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 = true;

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');
      }

      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);
        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(({ data }) => {
        if (cancelled || !mountedRef.current) return;
        const currentUser = data.session?.user ?? null;
        currentUserIdRef.current = currentUser?.id ?? null;
        setUser(currentUser);
      })
      .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((_event, session) => {
      const nextUser = session?.user ?? null;

      // Supabase warns against running additional async Supabase calls directly
      // inside onAuthStateChange. Keep this callback synchronous and let the
      // profile-loading effect below react to the user change. Deferring the
      // state write also prevents auth event deadlocks during sign-in.
      setTimeout(() => {
        if (!mountedRef.current) return;
        currentUserIdRef.current = nextUser?.id ?? null;
        setUser(nextUser);
      }, 0);
    });

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

  useEffect(() => {
    if (BYPASS_AUTH || initializing) return;

    if (!user) {
      activeProfileFetchRef.current += 1;
      currentUserIdRef.current = null;
      setProfileState(null);
      setProfileStatus('missing');
      return;
    }

    currentUserIdRef.current = user.id;
    void fetchProfile(user.id, { showLoading: true });
  }, [fetchProfile, initializing, user?.id]);

  const signOut = useCallback(async () => {
    activeProfileFetchRef.current += 1;
    setProfileState(null);
    setProfileStatus('missing');
    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 (
      <div
        style={{
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          height: '100vh',
        }}
      >
        <div
          style={{
            width: '40px',
            height: '40px',
            border: '3px solid #f3f3f3',
            borderTop: '3px solid #6d28d9',
            borderRadius: '50%',
            animation: 'spin 1s linear infinite',
          }}
        />
        <style>
          {`
            @keyframes spin {
              0% { transform: rotate(0deg); }
              100% { transform: rotate(360deg); }
            }
          `}
        </style>
      </div>
    );
  }

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