'use client';

import {
  createContext,
  useContext,
  useEffect,
  useState,
  useCallback,
  useRef,
  ReactNode,
} from 'react';
import { usePathname } from 'next/navigation';
import { api } from '@/lib/api';
import {
  broadcastSessionRevoked,
  hasStoredAccessToken,
  isPortalPath,
  onSessionExpired,
  SESSION_REVOKED_KEY,
  shouldShowSessionExpiredModal,
} from '@/lib/sessionAuth';

import type { Gender } from '@ehandyhire/shared';

export interface User {
  id: string;
  email?: string;
  phone?: string;
  firstName: string;
  lastName: string;
  gender?: Gender | null;
  role: 'CUSTOMER' | 'WORKER' | 'ADMIN';
  avatarUrl?: string;
  emailVerified?: boolean;
  phoneVerified?: boolean;
  deletionRequestedAt?: string | null;
  deletionScheduledFor?: string | null;
  deletionReason?: string | null;
  customerProfile?: Record<string, unknown>;
  workerProfile?: Record<string, unknown>;
}

interface AuthContextType {
  user: User | null;
  token: string | null;
  isLoading: boolean;
  sessionExpired: boolean;
  dismissSessionExpired: () => void;
  login: (identifier: string, password: string) => Promise<User>;
  loginWithGoogle: (idToken: string) => Promise<User>;
  register: (data: RegisterData) => Promise<User>;
  logout: () => Promise<void>;
  refreshUser: () => Promise<void>;
}

interface RegisterData {
  email?: string;
  phone?: string;
  password?: string;
  googleIdToken?: string;
  firstName: string;
  lastName: string;
  displayName?: string;
  gender?: Gender;
  role: 'CUSTOMER' | 'WORKER';
  serviceAreas?: string[];
  categoryIds?: string[];
  otherSkills?: string;
  isStudent?: boolean;
  hourlyRate?: number;
  rateType?: 'HOURLY' | 'DAILY' | 'WEEKLY' | 'MONTHLY';
  otpCode?: string;
}

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

export { AuthContext };

export function AuthProvider({ children }: { children: ReactNode }) {
  const pathname = usePathname();
  const [user, setUser] = useState<User | null>(null);
  const [token, setToken] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [sessionExpired, setSessionExpired] = useState(false);
  const userRef = useRef<User | null>(null);
  const tokenRef = useRef<string | null>(null);

  userRef.current = user;
  tokenRef.current = token;

  const clearSession = useCallback(() => {
    localStorage.removeItem('accessToken');
    localStorage.removeItem('refreshToken');
    setUser(null);
    setToken(null);
  }, []);

  const markSessionExpired = useCallback(() => {
    if (!shouldShowSessionExpiredModal()) return;
    setSessionExpired(true);
  }, []);

  const dismissSessionExpired = useCallback(() => {
    setSessionExpired(false);
  }, []);

  const handleSessionExpired = useCallback(() => {
    markSessionExpired();
    clearSession();
  }, [clearSession, markSessionExpired]);

  const syncSessionWithStorage = useCallback(() => {
    const hadSession = Boolean(userRef.current || tokenRef.current);
    if (!hadSession || hasStoredAccessToken()) return;
    if (!shouldShowSessionExpiredModal()) {
      clearSession();
      return;
    }
    setSessionExpired(true);
    setUser(null);
    setToken(null);
  }, [clearSession]);

  useEffect(() => {
    return onSessionExpired(handleSessionExpired);
  }, [handleSessionExpired]);

  const refreshUser = useCallback(async () => {
    const stored = localStorage.getItem('accessToken');
    try {
      if (!stored) {
        setIsLoading(false);
        return;
      }
      setToken(stored);
      const res = await api<{ data: User }>('/auth/me', { token: stored });
      setUser(res.data);
      setSessionExpired(false);
    } catch {
      if (stored) {
        markSessionExpired();
      }
      clearSession();
    } finally {
      setIsLoading(false);
    }
  }, [clearSession, markSessionExpired]);

  useEffect(() => {
    const onStorage = (event: StorageEvent) => {
      if (event.key === 'accessToken' || event.key === SESSION_REVOKED_KEY) {
        syncSessionWithStorage();
      }
      if (event.key === 'accessToken' && event.newValue && !userRef.current) {
        void refreshUser();
      }
    };

    const onVisible = () => {
      if (document.visibilityState === 'visible') {
        syncSessionWithStorage();
      }
    };

    window.addEventListener('storage', onStorage);
    document.addEventListener('visibilitychange', onVisible);
    window.addEventListener('focus', syncSessionWithStorage);

    return () => {
      window.removeEventListener('storage', onStorage);
      document.removeEventListener('visibilitychange', onVisible);
      window.removeEventListener('focus', syncSessionWithStorage);
    };
  }, [syncSessionWithStorage, refreshUser]);

  useEffect(() => {
    refreshUser();
  }, [refreshUser]);

  useEffect(() => {
    if (!isPortalPath(pathname)) {
      setSessionExpired(false);
    }
  }, [pathname]);

  const applyAuthSession = (payload: {
    user: User;
    accessToken: string;
    refreshToken: string;
  }): User => {
    localStorage.setItem('accessToken', payload.accessToken);
    localStorage.setItem('refreshToken', payload.refreshToken);
    setToken(payload.accessToken);
    setUser(payload.user);
    setSessionExpired(false);
    return payload.user;
  };

  const login = async (identifier: string, password: string): Promise<User> => {
    const res = await api<{ data: { user: User; accessToken: string; refreshToken: string } }>(
      '/auth/login',
      { method: 'POST', body: JSON.stringify({ identifier, password }) }
    );
    return applyAuthSession(res.data);
  };

  const loginWithGoogle = async (idToken: string): Promise<User> => {
    const res = await api<{ data: { user: User; accessToken: string; refreshToken: string } }>(
      '/auth/google',
      { method: 'POST', body: JSON.stringify({ idToken }) }
    );
    return applyAuthSession(res.data);
  };

  const register = async (data: RegisterData): Promise<User> => {
    const res = await api<{ data: { user: User; accessToken: string; refreshToken: string } }>(
      '/auth/register',
      { method: 'POST', body: JSON.stringify(data) }
    );
    return applyAuthSession(res.data);
  };

  const logout = async () => {
    try {
      await api('/auth/logout', { method: 'POST' });
    } catch {
      // ignore
    }
    broadcastSessionRevoked();
    clearSession();
    setSessionExpired(false);
  };

  return (
    <AuthContext.Provider
      value={{
        user,
        token,
        isLoading,
        sessionExpired,
        dismissSessionExpired,
        login,
        loginWithGoogle,
        register,
        logout,
        refreshUser,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
}

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