import React, {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
  type ReactNode,
} from "react";

import { setAuthToken } from "../lib/api";
import {
  login as loginService,
  register as registerService,
  type AuthUser,
} from "../services/auth.service";
import {
  getMyStatus,
  type EtaResult,
  type InboxNotification,
  type UserStatusResponse,
} from "../services/tracking.service";
// expo-notifications NO se importa: en Expo Go SDK 53+ se rompe al cargar
// porque intenta auto-registrar un push token. Para push real necesitamos
// un Development Build. Por ahora usamos un Toast in-app vía el callback
// onNewNotification.

const POLL_INTERVAL_MS = 30_000; // 30s, igual que el simulador del backend

interface AppContextValue {
  user: AuthUser | null;
  eta: EtaResult | null;
  notifications: InboxNotification[];
  route: UserStatusResponse["route"] | null;
  loading: boolean;
  toast: InboxNotification | null;
  dismissToast: () => void;
  login: (email: string, password: string) => Promise<void>;
  register: (name: string, email: string, password: string) => Promise<void>;
  logout: () => void;
  refreshStatus: () => Promise<void>;
}

const AppContext = createContext<AppContextValue | null>(null);

export const useApp = (): AppContextValue => {
  const ctx = useContext(AppContext);
  if (!ctx) throw new Error("useApp must be used within AppProvider");
  return ctx;
};

export const AppProvider = ({ children }: { children: ReactNode }) => {
  const [user, setUser] = useState<AuthUser | null>(null);
  const [eta, setEta] = useState<EtaResult | null>(null);
  const [notifications, setNotifications] = useState<InboxNotification[]>([]);
  const [route, setRoute] = useState<UserStatusResponse["route"] | null>(null);
  const [loading, setLoading] = useState(false);

  const pollingRef = useRef<ReturnType<typeof setInterval> | null>(null);
  // IDs de notificaciones que ya disparamos como push local — evita duplicados
  const shownNotificationIds = useRef<Set<string>>(new Set());
  // Bandera: en el primer refresh tras login no disparamos el historial
  // entero como notificaciones; solo lo marcamos como "ya visto".
  const initialRefreshDoneRef = useRef(false);

  // Última notificación nueva — la consume el Toast in-app
  const [toast, setToast] = useState<InboxNotification | null>(null);
  const dismissToast = useCallback(() => setToast(null), []);

  const refreshStatus = useCallback(async () => {
    setLoading(true);
    try {
      const res = await getMyStatus();
      setEta(res.eta);
      setRoute(res.route);
      setNotifications(res.notifications);

      // Detecta notificaciones nuevas (no vistas antes) y muestra la más
      // reciente como Toast in-app. En el primer refresh no toasteamos
      // para no inundar al usuario con el historial.
      const newOnes: InboxNotification[] = [];
      for (const n of res.notifications) {
        if (!shownNotificationIds.current.has(n.id)) {
          shownNotificationIds.current.add(n.id);
          newOnes.push(n);
        }
      }
      if (initialRefreshDoneRef.current && newOnes.length > 0) {
        setToast(newOnes[0]); // la primera del array es la más reciente
      }
      initialRefreshDoneRef.current = true;
    } catch (err) {
      console.error("Status refresh failed:", err);
    } finally {
      setLoading(false);
    }
  }, []);

  const login = useCallback(async (email: string, password: string) => {
    setLoading(true);
    try {
      const res = await loginService({ email, password });
      setUser(res.user);
    } finally {
      setLoading(false);
    }
  }, []);

  const register = useCallback(
    async (name: string, email: string, password: string) => {
      setLoading(true);
      try {
        // 1) crear cuenta
        await registerService({ name, email, password });
        // 2) hacer login inmediato para guardar el token y el user
        const res = await loginService({ email, password });
        setUser(res.user);
      } finally {
        setLoading(false);
      }
    },
    [],
  );

  const logout = useCallback(() => {
    setAuthToken(null);
    setUser(null);
    setEta(null);
    setRoute(null);
    setNotifications([]);
    setToast(null);
    // Reset del tracking de notificaciones para que la próxima sesión
    // arranque limpia (sin disparar el historial viejo).
    shownNotificationIds.current.clear();
    initialRefreshDoneRef.current = false;
  }, []);

  // Polling controlado: arranca al loguearse, se detiene al deslogearse.
  useEffect(() => {
    if (!user) {
      if (pollingRef.current) {
        clearInterval(pollingRef.current);
        pollingRef.current = null;
      }
      return;
    }

    void refreshStatus();
    pollingRef.current = setInterval(() => {
      void refreshStatus();
    }, POLL_INTERVAL_MS);

    return () => {
      if (pollingRef.current) {
        clearInterval(pollingRef.current);
        pollingRef.current = null;
      }
    };
  }, [user, refreshStatus]);

  return (
    <AppContext.Provider
      value={{
        user,
        eta,
        notifications,
        route,
        loading,
        toast,
        dismissToast,
        login,
        register,
        logout,
        refreshStatus,
      }}
    >
      {children}
    </AppContext.Provider>
  );
};
