import { createContext, useContext, useCallback } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import type { ReactNode } from "react";
import { pb } from "@/lib/pocketbase";
import type { AuthRecord } from "pocketbase";

/**
 * Represents the shape of the authenticated user object from PocketBase.
 */
/**
 * Context value for authentication state and actions.
 */
export interface AuthContextValue {
  user: AuthRecord | null;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<void>;
  signup: (
    email: string,
    password: string,
    passwordConfirm: string,
  ) => Promise<void>;
  logout: () => Promise<void>;
}

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

/**
 * Fetches the currently authenticated user from PocketBase.
 */
async function fetchUser(): Promise<AuthRecord | null> {
  if (pb.authStore.isValid) {
    return pb.authStore.record;
  }
  return null;
}

/**
 * Provider for authentication context, using TanStack Query for state management.
 */
export function AuthProvider({ children }: { children: ReactNode }) {
  const queryClient = useQueryClient();
  const { data: user, isLoading } = useQuery({
    queryKey: ["auth", "user"],
    queryFn: fetchUser,
  });

  const loginMutation = useMutation({
    mutationFn: async ({
      email,
      password,
    }: {
      email: string;
      password: string;
    }) => {
      await pb.collection("users").authWithPassword(email, password);
      return fetchUser();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["auth", "user"] });
    },
  });

  const signupMutation = useMutation({
    mutationFn: async ({
      email,
      password,
      passwordConfirm,
    }: {
      email: string;
      password: string;
      passwordConfirm: string;
    }) => {
      await pb.collection("users").create({ email, password, passwordConfirm });
      await pb.collection("users").authWithPassword(email, password);
      return fetchUser();
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["auth", "user"] });
    },
  });

  const logoutMutation = useMutation({
    mutationFn: async () => {
      pb.authStore.clear();
      return null;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ["auth", "user"] });
    },
  });

  const login = useCallback(
    async (email: string, password: string) => {
      await loginMutation.mutateAsync({ email, password });
    },
    [loginMutation],
  );

  const signup = useCallback(
    async (email: string, password: string, passwordConfirm: string) => {
      await signupMutation.mutateAsync({ email, password, passwordConfirm });
    },
    [signupMutation],
  );

  const logout = useCallback(async () => {
    await logoutMutation.mutateAsync();
  }, [logoutMutation]);

  return (
    <AuthContext.Provider
      value={{ user: user ?? null, isLoading, login, signup, logout }}
    >
      {children}
    </AuthContext.Provider>
  );
}

/**
 * Hook to access authentication context.
 * Throws if used outside of AuthProvider.
 */
export function useAuth(): AuthContextValue {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error("useAuth must be used within an AuthProvider");
  return ctx;
}
