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

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>;
  importPeriodicExpenses: (file: File) => Promise<ImportResult>;
  importExpenses: (file: File) => Promise<ImportExpensesResult>;
}

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

export function ExpensesProvider({ children }: { children: ReactNode }) {
  const [statusFilter, setStatusFilter] = useState("PENDING");
  const [page, setPage] = useState(1);
  const pageSize = 10;
  const [periodicExpenseFilter, setPeriodicExpenseFilter] = useState<number | undefined>(undefined);
  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);
  }, [periodicExpenseFilter, searchQuery]);

  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 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],
  );

  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,
        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;
}
