export type Quote = {
  id: number;
  timeStamp: string;
  buy: string;
  sell: string;
  type: "BELO" | "BLUE" | "BNA";
  difference: string;
  percentage: string;
  previousDayDifference: string;
  previousDayPercentage: string;
};

export type FetchResult = {
  type: string;
  status: string;
  error?: string;
};

export type HistoryQuote = {
  id: number;
  timeStamp: string;
  buy: string;
  sell: string;
  type: "BELO" | "BLUE" | "BNA";
};

export type DailyMinMax = {
  date: string;
  minBuy: number;
  maxBuy: number;
  minSell: number;
  maxSell: number;
};

export type DailyQuote = {
  date: string;
  buy: number;
  sell: number;
};

async function fetcher<T>(url: string): Promise<T> {
  const res = await fetch(url, { credentials: "include" });
  if (!res.ok) {
    throw new Error(`API error: ${res.status} ${res.statusText}`);
  }
  return res.json();
}

async function mutator<T>(url: string, method: string, body?: unknown): Promise<T> {
  const res = await fetch(url, {
    method,
    headers: { "Content-Type": "application/json" },
    body: body ? JSON.stringify(body) : undefined,
    credentials: "include",
  });
  if (!res.ok) {
    throw new Error(`API error: ${res.status} ${res.statusText}`);
  }
  return res.json();
}

export function getQuotes(): Promise<Quote[]> {
  return fetcher<Quote[]>("/api/quotes");
}

export function fetchQuotes(): Promise<FetchResult[]> {
  return fetcher<FetchResult[]>("/api/quotes/fetch");
}

export function getQuoteHistory(
  type: string,
  startDate: string,
  endDate: string,
): Promise<HistoryQuote[]> {
  const params = new URLSearchParams({ startDate, endDate });
  return fetcher<HistoryQuote[]>(`/api/quotes/${type}/history?${params}`);
}

export function getDailyMinMax(
  type: string,
  startDate: string,
  endDate: string,
): Promise<DailyMinMax[]> {
  const params = new URLSearchParams({ startDate, endDate });
  return fetcher<DailyMinMax[]>(`/api/quotes/${type}/min-max?${params}`);
}

export function getDailyQuotes(
  type: string,
  startDate: string,
  endDate: string,
): Promise<DailyQuote[]> {
  const params = new URLSearchParams({ startDate, endDate });
  return fetcher<DailyQuote[]>(`/api/quotes/${type}/daily?${params}`);
}

// Expenses

export type PeriodicExpense = {
  id: number;
  description: string;
  defaultDueDay: number;
  defaultAmount: string;
  periods: number[];
  isDeleted: boolean;
  createdAt: string;
  currentMonthApplicable?: boolean;
};

export type Expense = {
  id: number;
  description: string;
  periodicExpenseId: number | null;
  year: number;
  month: number;
  amount: string;
  amountPayed: string | null;
  status: "PENDING" | "PAYED";
  dueDate: string;
  paymentDate: string | null;
  periodicExpense?: PeriodicExpense | null;
};

export type CreatePeriodicExpenseInput = {
  description: string;
  defaultDueDay: number;
  defaultAmount: number;
  periods: number[];
};

export type CreateNonPeriodicExpenseInput = {
  description: string;
  amount: number;
  dueDate: string;
};

export type PayExpenseInput = {
  amountPayed: number;
  paymentDate?: string;
};

export function getPeriodicExpenses(): Promise<PeriodicExpense[]> {
  return fetcher<PeriodicExpense[]>("/api/periodic-expenses");
}

export function createPeriodicExpense(data: CreatePeriodicExpenseInput): Promise<PeriodicExpense> {
  return mutator<PeriodicExpense>("/api/periodic-expenses", "POST", data);
}

export function updatePeriodicExpense(id: number, data: CreatePeriodicExpenseInput): Promise<PeriodicExpense> {
  return mutator<PeriodicExpense>(`/api/periodic-expenses/${id}`, "PUT", data);
}

export function deletePeriodicExpense(id: number): Promise<void> {
  return mutator<void>(`/api/periodic-expenses/${id}`, "DELETE");
}

export function generateMonthlyExpense(id: number): Promise<Expense> {
  return mutator<Expense>(`/api/periodic-expenses/${id}/generate-month`, "POST");
}

export type MonthlyExpensesTotal = { total: number };

export function getMonthlyPayedTotal(year?: number, month?: number): Promise<MonthlyExpensesTotal> {
  const params = new URLSearchParams();
  if (year) params.set("year", String(year));
  if (month) params.set("month", String(month));
  return fetcher<MonthlyExpensesTotal>(`/api/expenses/monthly-total?${params}`);
}

export type PaginatedResponse<T> = {
  data: T[];
  total: number;
  page: number;
  pageSize: number;
};

export type MonthlyTotals = {
  payed: number;
  pending: number;
};

export function getExpenses(
  status?: string,
  page?: number,
  pageSize?: number,
  periodicExpenseId?: number,
  search?: string,
): Promise<PaginatedResponse<Expense>> {
  const params = new URLSearchParams();
  if (status) params.set("status", status);
  if (page) params.set("page", String(page));
  if (pageSize) params.set("pageSize", String(pageSize));
  if (periodicExpenseId) params.set("periodicExpenseId", String(periodicExpenseId));
  if (search) params.set("search", search);
  const qs = params.toString();
  return fetcher<PaginatedResponse<Expense>>(`/api/expenses${qs ? `?${qs}` : ""}`);
}

export function getTotalPending(): Promise<MonthlyExpensesTotal> {
  return fetcher<MonthlyExpensesTotal>("/api/expenses/pending-total");
}

export function getMonthlyTotals(year?: number, month?: number): Promise<MonthlyTotals> {
  const params = new URLSearchParams();
  if (year) params.set("year", String(year));
  if (month) params.set("month", String(month));
  return fetcher<MonthlyTotals>(`/api/expenses/totals?${params}`);
}

export function createNonPeriodicExpense(data: CreateNonPeriodicExpenseInput): Promise<Expense> {
  return mutator<Expense>("/api/expenses", "POST", data);
}

export function payExpense(id: number, data: PayExpenseInput): Promise<Expense> {
  return mutator<Expense>(`/api/expenses/${id}/pay`, "PUT", data);
}

export type ImportResult = {
  imported: number;
  skipped: number;
  skippedNoPeriods: number;
  skippedDuplicate: number;
  errors?: string[];
};

export type ImportExpensesResult = {
  imported: number;
  skipped: number;
  errors?: string[];
};

export function importExpenses(file: File): Promise<ImportExpensesResult> {
  const formData = new FormData();
  formData.append("file", file);
  return fetch("/api/expenses/import", {
    method: "POST",
    body: formData,
    credentials: "include",
  }).then((r) => {
    if (!r.ok) throw new Error(`API error: ${r.status} ${r.statusText}`);
    return r.json();
  });
}

export function importPeriodicExpenses(file: File): Promise<ImportResult> {
  const formData = new FormData();
  formData.append("file", file);
  return fetch("/api/periodic-expenses/import", {
    method: "POST",
    body: formData,
    credentials: "include",
  }).then((r) => {
    if (!r.ok) throw new Error(`API error: ${r.status} ${r.statusText}`);
    return r.json();
  });
}
