import {
  createContext,
  type ReactNode,
  useCallback,
  useContext,
  useEffect,
  useRef,
  useState,
} from "react";
import type {
  CreateNonPeriodicExpenseInput,
  CreatePeriodicExpenseInput,
  Expense,
  ImportExpensesResult,
  ImportResult,
  MonthlyExpensesTotal,
  MonthlyTotals,
  PaginatedResponse,
  PayExpenseInput,
  PeriodicExpense,
  UpdateExpenseInput,
} from "@/lib/api";
import {
  useCreateNonPeriodicExpense,
  useCreatePeriodicExpense,
  useDeletePeriodicExpense,
  useExpenses,
  useGenerateMonthlyExpense,
  useImportExpenses,
  useImportPeriodicExpenses,
  useMonthlyTotals,
  usePayExpense,
  usePeriodicExpenses,
  useTotalPending,
  useUpdateExpense,
  useUpdatePeriodicExpense,
} from "@/lib/queries";

interface ExpensesContextValue {
  periodicExpenses: PeriodicExpense[];
  isLoadingPeriodic: boolean;

  expenses: PaginatedResponse<Expense>;
  isLoadingExpenses: boolean;
  statusFilter: string;
  setStatusFilter: (status: string) => void;
  page: number;
  setPage: (page: number) => void;
  pageSize: number;
  periodicExpenseFilter: number | undefined;
  setPeriodicExpenseFilter: (id: number | undefined) => void;
  searchInput: string;
  setSearchInput: (value: string) => void;

  monthlyTotals: MonthlyTotals | undefined;
  isLoadingTotals: boolean;
  totalPending: MonthlyExpensesTotal | undefined;
  isLoadingTotalPending: boolean;

  createPeriodicExpense: (
    data: CreatePeriodicExpenseInput,
  ) => Promise<PeriodicExpense>;
  updatePeriodicExpense: (
    id: number,
    data: CreatePeriodicExpenseInput,
  ) => Promise<void>;
  deletePeriodicExpense: (id: number) => Promise<void>;
  generateMonthlyExpense: (id: number) => Promise<Expense>;
  createNonPeriodicExpense: (
    data: CreateNonPeriodicExpenseInput,
  ) => Promise<void>;
  payExpense: (id: number, data: PayExpenseInput) => Promise<void>;
  updateExpense: (id: number, data: UpdateExpenseInput) => Promise<void>;
  importPeriodicExpenses: (file: File) => Promise<ImportResult>;
  importExpenses: (file: File) => Promise<ImportExpensesResult>;
}

const ExpensesContext = createContext<ExpensesContextValue | null>(null);

export function ExpensesProvider({
  children,
  initialPeriodicExpenseId,
}: {
  children: ReactNode;
  initialPeriodicExpenseId?: number;
}) {
  const [statusFilter, setStatusFilter] = useState("PENDING");
  const [page, setPage] = useState(1);
  const pageSize = 10;
  const [periodicExpenseFilter, setPeriodicExpenseFilter] = useState<
    number | undefined
  >(initialPeriodicExpenseId);
  const [searchInput, setSearchInput] = useState("");
  const [searchQuery, setSearchQuery] = useState("");

  const searchTimerRef = useRef<ReturnType<typeof setTimeout>>(undefined);

  useEffect(() => {
    if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
    searchTimerRef.current = setTimeout(() => {
      setSearchQuery(searchInput);
    }, 300);
    return () => {
      if (searchTimerRef.current) clearTimeout(searchTimerRef.current);
    };
  }, [searchInput]);

  useEffect(() => {
    setPage(1);
  }, []);

  const { data: periodicExpenses = [], isLoading: isLoadingPeriodic } =
    usePeriodicExpenses();
  const {
    data: expenses = { data: [], total: 0, page: 1, pageSize: 10 },
    isLoading: isLoadingExpenses,
  } = useExpenses(
    statusFilter,
    page,
    pageSize,
    periodicExpenseFilter,
    searchQuery || undefined,
  );

  const now = new Date();
  const { data: monthlyTotals, isLoading: isLoadingTotals } = useMonthlyTotals(
    now.getFullYear(),
    now.getMonth() + 1,
  );
  const { data: totalPending, isLoading: isLoadingTotalPending } =
    useTotalPending();

  const createPeriodicMutation = useCreatePeriodicExpense();
  const updatePeriodicMutation = useUpdatePeriodicExpense();
  const deletePeriodicMutation = useDeletePeriodicExpense();
  const generateMonthlyMutation = useGenerateMonthlyExpense();
  const createExpenseMutation = useCreateNonPeriodicExpense();
  const payExpenseMutation = usePayExpense();
  const updateExpenseMutation = useUpdateExpense();
  const importPeriodicMutation = useImportPeriodicExpenses();
  const importExpensesMutation = useImportExpenses();

  const importPeriodicExpensesFn = useCallback(
    async (file: File): Promise<ImportResult> => {
      return importPeriodicMutation.mutateAsync(file);
    },
    [importPeriodicMutation],
  );

  const importExpensesFn = useCallback(
    async (file: File): Promise<ImportExpensesResult> => {
      return importExpensesMutation.mutateAsync(file);
    },
    [importExpensesMutation],
  );

  const createPeriodicExpense = useCallback(
    async (data: CreatePeriodicExpenseInput): Promise<PeriodicExpense> => {
      return createPeriodicMutation.mutateAsync(data);
    },
    [createPeriodicMutation],
  );

  const updatePeriodicExpenseFn = useCallback(
    async (id: number, data: CreatePeriodicExpenseInput) => {
      await updatePeriodicMutation.mutateAsync({ id, data });
    },
    [updatePeriodicMutation],
  );

  const deletePeriodicExpenseFn = useCallback(
    async (id: number) => {
      await deletePeriodicMutation.mutateAsync(id);
    },
    [deletePeriodicMutation],
  );

  const generateMonthlyExpenseFn = useCallback(
    async (id: number): Promise<Expense> => {
      return generateMonthlyMutation.mutateAsync(id);
    },
    [generateMonthlyMutation],
  );

  const createNonPeriodicExpenseFn = useCallback(
    async (data: CreateNonPeriodicExpenseInput) => {
      await createExpenseMutation.mutateAsync(data);
    },
    [createExpenseMutation],
  );

  const payExpenseFn = useCallback(
    async (id: number, data: PayExpenseInput) => {
      await payExpenseMutation.mutateAsync({ id, data });
    },
    [payExpenseMutation],
  );

  const updateExpenseFn = useCallback(
    async (id: number, data: UpdateExpenseInput) => {
      await updateExpenseMutation.mutateAsync({ id, data });
    },
    [updateExpenseMutation],
  );

  return (
    <ExpensesContext
      value={{
        periodicExpenses,
        isLoadingPeriodic,
        expenses,
        isLoadingExpenses,
        statusFilter,
        setStatusFilter,
        page,
        setPage,
        pageSize,
        periodicExpenseFilter,
        setPeriodicExpenseFilter,
        searchInput,
        setSearchInput,
        monthlyTotals,
        isLoadingTotals,
        totalPending,
        isLoadingTotalPending,
        createPeriodicExpense,
        updatePeriodicExpense: updatePeriodicExpenseFn,
        deletePeriodicExpense: deletePeriodicExpenseFn,
        generateMonthlyExpense: generateMonthlyExpenseFn,
        createNonPeriodicExpense: createNonPeriodicExpenseFn,
        payExpense: payExpenseFn,
        updateExpense: updateExpenseFn,
        importPeriodicExpenses: importPeriodicExpensesFn,
        importExpenses: importExpensesFn,
      }}
    >
      {children}
    </ExpensesContext>
  );
}

export function useExpensesContext() {
  const ctx = useContext(ExpensesContext);
  if (!ctx)
    throw new Error("useExpensesContext must be used within ExpensesProvider");
  return ctx;
}
