import React, {
  createContext,
  useContext,
  useState,
  useCallback,
  useEffect,
} from "react";
import { useUser, useAuth } from "@clerk/clerk-expo";
import {
  getHydrationByDate,
  saveDailyHydration,
  type HydrationEntry,
} from "../api/hydration";
import log from "../utils/logger";

interface HydrationContextValue {
  hydration: HydrationEntry | null;
  loading: boolean;
  error: Error | null;
  waterGoal: number;
  todayTotal: number;
  percentage: number;
  addWater: (amount: number) => Promise<void>;
  resetToday: () => Promise<void>;
  setWaterGoal: (goal: number) => void;
  refetch: () => Promise<void>;
}

const HydrationContext = createContext<HydrationContextValue | undefined>(
  undefined,
);

export function HydrationProvider({ children }: { children: React.ReactNode }) {
  const { user } = useUser();
  const { getToken } = useAuth();
  const [hydration, setHydration] = useState<HydrationEntry | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  const [waterGoal, setWaterGoal] = useState(2000); // Default goal: 2000ml

  const today = new Date().toISOString().split("T")[0];

  const fetchTodayHydration = useCallback(async () => {
    if (!user?.id) return;

    try {
      setLoading(true);
      setError(null);

      const token = await getToken();
      const data = await getHydrationByDate(today, token);

      if (data) {
        setHydration(data);
        if (data.waterGoal) {
          setWaterGoal(data.waterGoal);
        }
      } else {
        // No data for today yet
        setHydration(null);
      }

      log.debug("Today's hydration fetched", { data });
    } catch (err) {
      const error = err instanceof Error ? err : new Error(String(err));
      log.error("Failed to fetch hydration", error);
      setError(error);
    } finally {
      setLoading(false);
    }
  }, [user?.id, getToken, today]);

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

  useEffect(() => {
    setHydration(null);
    setError(null);
    setLoading(false);
    setWaterGoal(2000);
    if (user?.id) {
      log.debug("Hydration state reset for user", { userId: user.id });
    } else {
      log.debug("Hydration state reset on sign-out");
    }
  }, [user?.id]);

  const addWater = useCallback(
    async (amount: number) => {
      if (!user?.id) return;

      try {
        const token = await getToken();
        const currentTotal = hydration?.totalWater || 0;
        const newTotal = currentTotal + amount;

        const entry = await saveDailyHydration(
          {
            date: today,
            totalWater: newTotal,
            waterGoal,
            entries: [
              ...(hydration?.entries || []),
              { amount, time: new Date().toISOString() },
            ],
          },
          token,
        );

        setHydration(entry);
        log.debug("Water added successfully", { amount, newTotal });
      } catch (err) {
        const error = err instanceof Error ? err : new Error(String(err));
        log.error("Failed to add water", error);
        setError(error);
        throw error; // Re-throw so UI can handle it
      }
    },
    [user?.id, getToken, today, hydration, waterGoal],
  );

  const resetToday = useCallback(async () => {
    if (!user?.id) return;

    try {
      const token = await getToken();

      await saveDailyHydration(
        {
          date: today,
          totalWater: 0,
          waterGoal,
          entries: [],
        },
        token,
      );

      setHydration({
        date: today,
        totalWater: 0,
        waterGoal,
        entries: [],
      });

      log.debug("Hydration reset for today");
    } catch (err) {
      const error = err instanceof Error ? err : new Error(String(err));
      log.error("Failed to reset hydration", error);
      setError(error);
      throw error;
    }
  }, [user?.id, getToken, today, waterGoal]);

  const todayTotal = hydration?.totalWater || 0;
  const percentage =
    waterGoal > 0 ? Math.round((todayTotal / waterGoal) * 100) : 0;

  const value: HydrationContextValue = {
    hydration,
    loading,
    error,
    waterGoal,
    todayTotal,
    percentage,
    addWater,
    resetToday,
    setWaterGoal,
    refetch: fetchTodayHydration,
  };

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

export function useHydration() {
  const context = useContext(HydrationContext);
  if (context === undefined) {
    throw new Error("useHydration must be used within a HydrationProvider");
  }
  return context;
}
