import { createContext, type PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { ApiError } from '../api/client';
import {
  getSessionMe,
  loginSession,
  logoutSession,
  refreshSession,
  type SessionLoginRequest,
  type SessionProfile
} from '../api/sessionApi';

export type SessionStatus = 'loading' | 'authenticated' | 'unauthenticated';

type SessionContextValue = {
  status: SessionStatus;
  profile: SessionProfile | null;
  error: string | null;
  login: (request: Omit<SessionLoginRequest, 'correlationId'> & { correlationId?: string }) => Promise<void>;
  refresh: () => Promise<void>;
  logout: () => Promise<void>;
  revalidate: () => Promise<void>;
};

const SessionContext = createContext<SessionContextValue | undefined>(undefined);

export function SessionProvider({ children }: PropsWithChildren) {
  const [status, setStatus] = useState<SessionStatus>('loading');
  const [profile, setProfile] = useState<SessionProfile | null>(null);
  const [error, setError] = useState<string | null>(null);

  const revalidate = useCallback(async () => {
    try {
      const me = await getSessionMe();
      if (me.isAuthenticated) {
        setProfile(me);
        setStatus('authenticated');
      } else {
        setProfile(null);
        setStatus('unauthenticated');
      }
      setError(null);
    } catch (err) {
      if (err instanceof ApiError && err.status === 401) {
        setProfile(null);
        setStatus('unauthenticated');
        setError(null);
        return;
      }

      const message = err instanceof Error ? err.message : 'Session validation failed.';
      setProfile(null);
      setStatus('unauthenticated');
      setError(message);
    }
  }, []);

  useEffect(() => {
    void revalidate();
  }, [revalidate]);

  const login = useCallback<SessionContextValue['login']>(
    async (request) => {
      setError(null);
      await loginSession({
        ...request,
        correlationId: request.correlationId && request.correlationId.length > 0 ? request.correlationId : createCorrelationId()
      });
      await revalidate();
    },
    [revalidate]
  );

  const refresh = useCallback(async () => {
    setError(null);
    await refreshSession();
    await revalidate();
  }, [revalidate]);

  const logout = useCallback(async () => {
    setError(null);
    try {
      await logoutSession();
    } finally {
      setProfile(null);
      setStatus('unauthenticated');
    }
  }, []);

  const value = useMemo<SessionContextValue>(
    () => ({
      status,
      profile,
      error,
      login,
      refresh,
      logout,
      revalidate
    }),
    [status, profile, error, login, refresh, logout, revalidate]
  );

  return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
}

export function useSessionContext(): SessionContextValue {
  const value = useContext(SessionContext);
  if (!value) {
    throw new Error('useSessionContext must be used within SessionProvider.');
  }

  return value;
}

function createCorrelationId(): string {
  if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
    return crypto.randomUUID();
  }

  return `corr-${Date.now()}-${Math.random().toString(16).slice(2)}`;
}
