import { create } from 'zustand';
import { api } from './api';

/**
 * Finance Module - Zustand Store
 *
 * Consumes Phase 1 backend endpoints (mounted at /api/finance):
 *   GET    /dashboard/summary
 *   GET    /expenses                   (list with status/category/date filters)
 *   GET    /expenses/categories
 *   GET    /expenses/summary           (Sprint 1b — B10)
 *   POST   /expenses                   (create)
 *   PATCH  /expenses/:id               (update — backend uses PATCH)
 *   DELETE /expenses/:id               (soft delete)
 *   PUT    /expenses/:id/approve       (Sprint 1b — B9)
 *   PUT    /expenses/:id/reject        (Sprint 1b — B9, body: { reason })
 *
 * Payroll-related actions remain (Phase 0) — they proxy to HR via
 * finance.controller.js's `/finance/payroll/*` rewrite. Kept on this store
 * to preserve the existing Finance-side payroll UX (filter/pay/cancel
 * actions) without churning imports.
 *
 * Deviation from dispatch: dispatch said `GET /api/finance/expenses/:id`
 * and `PUT /api/finance/expenses/:id`. The Phase 1 backend exposes
 * `PATCH /api/finance/expenses/:id` only. The edit page prefills by
 * fetching the list endpoint with `limit=500` and matching by id.
 */

// =========================
// Types
// =========================

/** Legacy Phase 0 — payroll data is owned by HR; Finance proxies to `/finance/payroll/*`. */
export interface PayrollRun {
  id: number;
  uid: string;
  period_month: number;
  period_year: number;
  description: string;
  status: 'draft' | 'calculated' | 'approved' | 'paid' | 'cancelled';
  total_gross: number;
  total_deductions: number;
  total_net: number;
}

/**
 * Payslip — matches the legacy Finance list shape. For the richer
 * `/hr/payslips/:id` shape (with email, department, designation, run_id,
 * period), see `client/src/store/hr.ts` instead.
 */
export interface Payslip {
  id: number;
  uid: string;
  staff_id: number;
  first_name: string;
  last_name: string;
  staff_number: string;
  period_month: number;
  period_year: number;
  basic_salary: number;
  housing_allowance: number;
  transport_allowance: number;
  medical_allowance: number;
  other_allowances: number;
  gross_salary: number;
  tax_deduction: number;
  pension_deduction: number;
  other_deductions: number;
  total_deductions: number;
  net_salary: number;
  status: string;
}

/** Phase 1 — aggregate metrics for /dashboard/finance. */
export interface FinanceDashboardSummary {
  collected_mtd: number;
  outstanding_fees: number;
  payroll_last_run: {
    id: number;
    period: string;
    status: string;
  } | null;
  expenses_mtd: number;
  pending_expenses: number;
  installments_due_14d?: number;
  open_invoices?: number;
  overdue_invoices?: number;
  pending_reconciliations?: number;
  upcoming_trips_30d?: number;
  student_groups_count?: number;
}

/** Phase 1 — expense category descriptors used by dropdowns. */
export interface ExpenseCategory {
  value: string;
  label: string;
}

/** Hard-coded fallback list — kept in sync with the backend's `VALID_CATEGORIES`. */
export const EXPENSE_CATEGORIES: ExpenseCategory[] = [
  { value: 'utilities', label: 'Utilities' },
  { value: 'supplies', label: 'Supplies' },
  { value: 'maintenance', label: 'Maintenance' },
  { value: 'transport', label: 'Transport' },
  { value: 'salaries', label: 'Salaries' },
  { value: 'other', label: 'Other' },
];

export const EXPENSE_STATUSES = [
  { value: 'pending', label: 'Pending' },
  { value: 'approved', label: 'Approved' },
  { value: 'paid', label: 'Paid' },
  { value: 'rejected', label: 'Rejected' },
] as const;

/** Phase 1 — single expense row. Mirrors the backend `expenses` table shape. */
export interface Expense {
  id: number;
  uid?: string;
  category: string;
  subcategory: string | null;
  description: string;
  amount: number;
  expense_date: string; // ISO YYYY-MM-DD or full ISO
  vendor: string | null;
  invoice_number: string | null;
  payment_method: string | null;
  status: string;
  receipt_image: string | null;
  created_by?: number;
  created_at?: string;
  updated_at?: string;
}

export interface ExpenseListResponse {
  expenses: Expense[];
  pagination: {
    total: number;
    limit: number;
    offset: number;
    returned: number;
  };
}

export interface ExpenseFilters {
  status?: string;
  category?: string;
  date_from?: string;
  date_to?: string;
  limit?: number;
  offset?: number;
}

/** Sprint 1b — B10: Finance dashboard "Approval queue" tile payload. */
export interface ExpenseSummary {
  pending_count: number;
  pending_amount: number;
  approved_this_month: { count: number; total: number };
  rejected_this_month: { count: number; total: number };
}

// =========================
// §10a — Operational core
// =========================

export interface Supplier {
  id: number;
  uid: string;
  name: string;
  code: string | null;
  contact_person: string | null;
  email: string | null;
  phone: string | null;
  address: string | null;
  city: string | null;
  country: string | null;
  tax_id: string | null;
  bank_details: string | null;
  notes: string | null;
  is_active: number;
}

export interface ChartAccount {
  id: number;
  uid: string;
  code: string;
  name: string;
  type: 'asset' | 'liability' | 'equity' | 'revenue' | 'expense';
  parent_id: number | null;
  color: string | null;
  is_active: number;
  children?: ChartAccount[];
}

export interface BankAccount {
  id: number;
  uid: string;
  name: string;
  bank_name: string | null;
  account_number: string | null;
  account_type: 'bank' | 'cash' | 'mobile_money';
  currency: string;
  opening_balance: number;
  current_balance: number;
  chart_account_id: number | null;
  is_active: number;
  chart_code?: string;
  chart_name?: string;
}

export interface ReconciliationLine {
  id: number;
  uid: string;
  payment_id: number | null;
  statement_date: string | null;
  statement_amount: number | null;
  statement_reference: string | null;
  match_status: 'matched' | 'unmatched' | 'ignored';
  payment_amount?: number | null;
  payment_date?: string | null;
  payment_reference?: string | null;
}

export interface Reconciliation extends BankAccount {
  account_name?: string;
  account_currency?: string;
  status: 'draft' | 'balanced' | 'discrepancy';
  period_start: string;
  period_end: string;
  statement_balance: number;
  book_balance: number;
  lines?: ReconciliationLine[];
}

export interface Invoice {
  id: number;
  uid: string;
  invoice_number: string;
  student_id: number;
  fee_group_id: number | null;
  plan_id: number | null;
  period_label: string | null;
  subtotal: number;
  discount_amount: number;
  total: number;
  paid_amount: number;
  currency: string;
  status: 'draft' | 'issued' | 'paid' | 'partial' | 'overdue' | 'cancelled';
  issued_at: string | null;
  due_date: string | null;
  notes: string | null;
  created_by: number | null;
  first_name?: string;
  last_name?: string;
  email?: string;
  fee_name?: string;
  fee_type?: string;
  lines?: Array<{
    id: number; amount: number; paid_amount: number; discount_amount: number;
    status: string; due_date: string | null;
    fee_name: string | null; fee_type: string | null;
  }>;
}

export interface Trip {
  id: number;
  uid: string;
  name: string;
  destination: string | null;
  departure_date: string | null;
  return_date: string | null;
  cost_per_student: number;
  total_capacity: number | null;
  status: 'draft' | 'published' | 'closed' | 'completed' | 'cancelled';
  description: string | null;
  billed_count?: number;
  paid_count?: number;
  collected?: number;
}

export interface TripStudentPayment {
  id: number;
  uid: string;
  trip_id: number;
  student_id: number;
  invoiced_amount: number;
  paid_amount: number;
  status: 'pending' | 'partial' | 'paid' | 'refunded' | 'cancelled';
  paid_at: string | null;
  first_name?: string;
  last_name?: string;
  email?: string;
}

export interface TripDetail extends Trip {
  students: TripStudentPayment[];
}

/** Fee plan template */
export interface FeePlanInstallment {
  id: number;
  uid: string;
  fee_plan_id: number;
  sequence: number;
  label: string;
  amount: number;
  due_date: string | null;
}

export interface FeePlan {
  id: number;
  uid: string;
  name: string;
  fee_group_id: number;
  total_amount: number;
  installments_count: number;
  start_date: string | null;
  end_date: string | null;
  academic_year: string | null;
  auto_apply_to_class_id: number | null;
  notes: string | null;
  fee_name?: string;
  fee_type?: string;
  class_name?: string | null;
  installments?: FeePlanInstallment[];
}

export interface Discount {
  id: number;
  uid: string;
  name: string;
  kind: 'percent' | 'fixed';
  value: number;
  scope: 'student' | 'group' | 'fee_group' | 'all';
  scope_id: number | null;
  reason: string | null;
  valid_from: string | null;
  valid_to: string | null;
  academic_year: string | null;
}

// §10b — groups + engine apply
export interface StudentGroup {
  id: number;
  uid: string;
  name: string;
  description: string | null;
  type: 'class' | 'trip' | 'hostel' | 'transport' | 'club' | 'fee' | 'custom';
  color: string | null;
  class_id: number | null;
  capacity: number | null;
  auto_track_class: number;
  member_count?: number;
  class_name?: string | null;
  members?: Array<{
    member_uid: string;
    joined_at: string;
    student_id: number;
    student_uid: string;
    first_name: string;
    last_name: string;
    email: string;
  }>;
}

// =========================
// Store
// =========================

interface FinanceStore {
  // -------- Phase 0 (legacy) state --------
  payrollRuns: PayrollRun[];
  payslips: Payslip[];
  loading: boolean;
  error: string | null;

  // -------- Phase 1 (new) state --------
  dashboardSummary: FinanceDashboardSummary | null;
  expenses: Expense[];
  expensesTotal: number;
  categories: string[];
  currentExpense: Expense | null;
  // Sprint 1b — B10
  expenseSummary: ExpenseSummary | null;

  // -------- §10a state --------
  suppliers: Supplier[];
  chartAccounts: ChartAccount[];
  bankAccounts: BankAccount[];
  reconciliations: Reconciliation[];
  invoices: Invoice[];
  currentInvoice: Invoice | null;
  trips: Trip[];
  currentTrip: TripDetail | null;
  plans: FeePlan[];
  currentPlan: FeePlan | null;
  discounts: Discount[];
  feeGroups: any[];

  // -------- Phase 0 actions --------
  fetchPayrollRuns: () => Promise<void>;
  fetchPayslips: (payrollRunId?: number) => Promise<void>;
  createPayrollRun: (data: { period_month: number; period_year: number; description?: string }) => Promise<void>;
  calculatePayroll: (id: number) => Promise<void>;
  payPayrollRun: (id: number) => Promise<void>;
  cancelPayrollRun: (id: number) => Promise<void>;

  // -------- Phase 1 actions --------
  fetchDashboardSummary: () => Promise<void>;
  fetchCategories: () => Promise<void>;
  fetchExpenses: (filters?: ExpenseFilters) => Promise<void>;
  /**
   * Single-expense prefiller for ExpenseEdit. Backend lacks GET /finance/expenses/:id,
   * so we fetch the list (limit=500) and find by id. Caches the row in `currentExpense`.
   */
  fetchExpense: (id: number) => Promise<Expense | null>;
  createExpense: (data: Partial<Expense>) => Promise<Expense>;
  updateExpense: (id: number, data: Partial<Expense>) => Promise<Expense | null>;
  deleteExpense: (id: number) => Promise<boolean>;

  // -------- Sprint 1b — B9 / B10 actions --------
  approveExpense: (id: number) => Promise<Expense | null>;
  rejectExpense: (id: number, reason: string) => Promise<Expense | null>;
  fetchExpenseSummary: () => Promise<void>;

  // -------- §10a actions --------
  fetchSuppliers: (params?: { search?: string; status?: 'active' | 'inactive' }) => Promise<void>;
  createSupplier: (data: Partial<Supplier>) => Promise<Supplier | null>;
  updateSupplier: (uid: string, data: Partial<Supplier>) => Promise<Supplier | null>;
  deleteSupplier: (uid: string) => Promise<boolean>;

  fetchChartAccounts: (params?: { flat?: boolean; type?: string }) => Promise<void>;
  createChartAccount: (data: Partial<ChartAccount>) => Promise<ChartAccount | null>;
  updateChartAccount: (uid: string, data: Partial<ChartAccount>) => Promise<ChartAccount | null>;
  deleteChartAccount: (uid: string) => Promise<boolean>;

  fetchBankAccounts: () => Promise<void>;
  createBankAccount: (data: Partial<BankAccount> & { chart_account_uid?: string }) => Promise<BankAccount | null>;
  updateBankAccount: (uid: string, data: Partial<BankAccount> & { chart_account_uid?: string | null }) => Promise<BankAccount | null>;
  deleteBankAccount: (uid: string) => Promise<boolean>;

  fetchReconciliations: (bank_account_uid?: string) => Promise<void>;
  fetchReconciliation: (uid: string) => Promise<Reconciliation | null>;
  reconcileBank: (data: {
    bank_account_uid: string;
    period_start: string;
    period_end: string;
    statement_balance: number;
    lines: Array<{ date: string; amount: number; reference?: string }>;
  }) => Promise<{ uid: string; matched: number; unmatched: number } | null>;
  resolveReconciliationLine: (
    recon_uid: string,
    line_id: number,
    action: 'ignore' | 'attach',
    payment_id?: number,
  ) => Promise<{ success: boolean; unmatched_count: number } | null>;

  fetchInvoices: (params?: { status?: string; student_uid?: string; fee_group_id?: number; period_label?: string; search?: string }) => Promise<void>;
  fetchInvoice: (uid: string) => Promise<Invoice | null>;
  createInvoice: (data: Partial<Invoice>) => Promise<Invoice | null>;
  updateInvoice: (uid: string, data: Partial<Invoice>) => Promise<Invoice | null>;
  deleteInvoice: (uid: string) => Promise<boolean>;
  sendInvoice: (uid: string) => Promise<boolean>;

  fetchTrips: (params?: { status?: string }) => Promise<void>;
  fetchTrip: (uid: string) => Promise<TripDetail | null>;
  createTrip: (data: Partial<Trip>) => Promise<Trip | null>;
  updateTrip: (uid: string, data: Partial<Trip>) => Promise<Trip | null>;
  deleteTrip: (uid: string) => Promise<boolean>;
  assignTripStudents: (
    trip_uid: string,
    body: { mode: 'ids' | 'class' | 'all'; student_uids?: string[]; class_id?: number },
  ) => Promise<{ added: number; skipped: number; total_assigned: number } | null>;
  payTripStudent: (
    trip_uid: string,
    student_uid: string,
    body: { amount: number; payment_method?: string; reference_number?: string; notes?: string },
  ) => Promise<{ success: boolean; status: string; new_paid_amount: number } | null>;

  fetchPlans: (params?: { fee_group_id?: number; academic_year?: string }) => Promise<void>;
  fetchPlan: (uid: string) => Promise<FeePlan | null>;
  createPlan: (data: Partial<FeePlan> & { installments?: FeePlanInstallment[] }) => Promise<{ uid: string; id: number } | null>;
  updatePlan: (uid: string, data: Partial<FeePlan>) => Promise<FeePlan | null>;
  deletePlan: (uid: string) => Promise<boolean>;
  replacePlanInstallments: (uid: string, installments: FeePlanInstallment[]) => Promise<boolean>;

  fetchDiscounts: (params?: { scope?: string; scope_id?: number; academic_year?: string }) => Promise<void>;
  fetchDiscount: (uid: string) => Promise<Discount | null>;
  createDiscount: (data: Partial<Discount>) => Promise<Discount | null>;
  updateDiscount: (uid: string, data: Partial<Discount>) => Promise<Discount | null>;
  deleteDiscount: (uid: string) => Promise<boolean>;
  fetchFeeGroups: () => Promise<void>;
}

export const useFinanceStore = create<FinanceStore>((set, get) => ({
  // -------- Phase 0 initial state --------
  payrollRuns: [],
  payslips: [],
  loading: false,
  error: null,

  // -------- Phase 1 initial state --------
  dashboardSummary: null,
  expenses: [],
  expensesTotal: 0,
  categories: EXPENSE_CATEGORIES.map((c) => c.value),
  currentExpense: null,
  // Sprint 1b — B10
  expenseSummary: null,

  // -------- §10a state --------
  suppliers: [],
  chartAccounts: [],
  bankAccounts: [],
  reconciliations: [],
  invoices: [],
  currentInvoice: null,
  trips: [],
  currentTrip: null,
  plans: [],
  currentPlan: null,
  discounts: [],
  feeGroups: [],

  // ============ PHASE 0 ACTIONS ============

  fetchPayrollRuns: async () => {
    set({ loading: true, error: null });
    try {
      const res = await api.get('/finance/payroll/runs');
      set({ payrollRuns: res.data, loading: false });
    } catch (err: any) {
      set({ loading: false, error: err?.message ?? 'Failed to fetch payroll runs' });
    }
  },

  fetchPayslips: async (payrollRunId) => {
    set({ loading: true, error: null });
    try {
      const res = await api.get('/finance/payroll/payslips', {
        params: payrollRunId ? { payroll_run_id: payrollRunId } : undefined,
      });
      set({ payslips: res.data, loading: false });
    } catch (err: any) {
      set({ loading: false, error: err?.message ?? 'Failed to fetch payslips' });
    }
  },

  createPayrollRun: async (data) => {
    set({ loading: true, error: null });
    try {
      await api.post('/finance/payroll/runs', data);
      await get().fetchPayrollRuns();
    } catch (err: any) {
      set({ loading: false, error: err?.message ?? 'Failed to create payroll run' });
      throw err;
    }
  },

  calculatePayroll: async (id) => {
    set({ loading: true, error: null });
    try {
      await api.post(`/finance/payroll/runs/${id}/calculate`);
      await get().fetchPayrollRuns();
    } catch (err: any) {
      set({ loading: false, error: err?.message ?? 'Failed to calculate payroll' });
      throw err;
    }
  },

  payPayrollRun: async (id) => {
    set({ loading: true, error: null });
    try {
      await api.post(`/finance/payroll/runs/${id}/pay`);
      await get().fetchPayrollRuns();
    } catch (err: any) {
      set({ loading: false, error: err?.message ?? 'Failed to mark payroll run as paid' });
      throw err;
    }
  },

  cancelPayrollRun: async (id) => {
    set({ loading: true, error: null });
    try {
      await api.post(`/finance/payroll/runs/${id}/cancel`);
      await get().fetchPayrollRuns();
    } catch (err: any) {
      set({ loading: false, error: err?.message ?? 'Failed to cancel payroll run' });
      throw err;
    }
  },

  // ============ PHASE 1 ACTIONS ============

  fetchDashboardSummary: async () => {
    try {
      const res = await api.get('/finance/dashboard/summary');
      set({ dashboardSummary: res.data, error: null });
    } catch (err: any) {
      set({
        error: err?.response?.data?.error || err?.message || 'Failed to load Finance dashboard',
      });
      console.error('Failed to fetch Finance dashboard summary', err);
    }
  },

  fetchCategories: async () => {
    try {
      const res = await api.get('/finance/expenses/categories');
      if (Array.isArray(res.data)) {
        set({ categories: res.data });
      }
    } catch (err) {
      // Fail soft — fall back to hard-coded list
      console.warn('Failed to fetch expense categories, using defaults', err);
    }
  },

  fetchExpenses: async (filters?: ExpenseFilters) => {
    try {
      const params = new URLSearchParams();
      if (filters?.status) params.set('status', filters.status);
      if (filters?.category) params.set('category', filters.category);
      if (filters?.date_from) params.set('date_from', filters.date_from);
      if (filters?.date_to) params.set('date_to', filters.date_to);
      params.set('limit', String(filters?.limit ?? 50));
      params.set('offset', String(filters?.offset ?? 0));
      const res = await api.get(`/finance/expenses?${params.toString()}`);
      const data: ExpenseListResponse = res.data;
      set({
        expenses: data.expenses || [],
        expensesTotal: data.pagination?.total ?? (data.expenses?.length || 0),
        error: null,
      });
    } catch (err: any) {
      set({
        error: err?.response?.data?.error || err?.message || 'Failed to load expenses',
      });
      console.error('Failed to fetch expenses', err);
    }
  },

  fetchExpense: async (id: number) => {
    try {
      let row: Expense | undefined = get().expenses.find((e) => e.id === id);
      if (!row) {
        const params = new URLSearchParams();
        params.set('limit', '500');
        params.set('offset', '0');
        const res = await api.get(`/finance/expenses?${params.toString()}`);
        const data: ExpenseListResponse = res.data;
        row = (data.expenses || []).find((e) => e.id === id);
        if (row) {
          set({ expenses: data.expenses, expensesTotal: data.pagination?.total || 0 });
        }
      }
      if (!row) {
        set({ error: `Expense #${id} not found` });
        return null;
      }
      set({ currentExpense: row, error: null });
      return row;
    } catch (err: any) {
      set({
        error: err?.response?.data?.error || err?.message || 'Failed to load expense',
      });
      console.error('Failed to fetch expense', err);
      return null;
    }
  },

  createExpense: async (data: Partial<Expense>) => {
    try {
      const res = await api.post('/finance/expenses', data);
      const created: Expense = res.data;
      // Optimistic prepend
      set((state) => ({
        expenses: [created, ...state.expenses],
        expensesTotal: state.expensesTotal + 1,
      }));
      return created;
    } catch (err: any) {
      const errorMsg = err?.response?.data?.error || err?.message || 'Failed to create expense';
      set({ error: errorMsg });
      throw new Error(errorMsg);
    }
  },

  // Backend uses PATCH (not PUT as the dispatch specified).
  updateExpense: async (id: number, data: Partial<Expense>) => {
    try {
      const res = await api.patch(`/finance/expenses/${id}`, data);
      const updated: Expense = res.data;
      set((state) => ({
        expenses: state.expenses.map((e) => (e.id === id ? updated : e)),
        currentExpense: state.currentExpense?.id === id ? updated : state.currentExpense,
      }));
      return updated;
    } catch (err: any) {
      const errorMsg = err?.response?.data?.error || err?.message || 'Failed to update expense';
      set({ error: errorMsg });
      return null;
    }
  },

  deleteExpense: async (id: number) => {
    try {
      await api.delete(`/finance/expenses/${id}`);
      set((state) => ({
        expenses: state.expenses.filter((e) => e.id !== id),
        expensesTotal: Math.max(0, state.expensesTotal - 1),
        currentExpense: state.currentExpense?.id === id ? null : state.currentExpense,
      }));
      return true;
    } catch (err: any) {
      const errorMsg = err?.response?.data?.error || err?.message || 'Failed to delete expense';
      set({ error: errorMsg });
      return false;
    }
  },

  // ============ SPRINT 1b — B9 / B10 ============

  /**
   * PUT /api/finance/expenses/:id/approve
   * Idempotency: backend returns 409 if row is not in 'pending'. We surface
   * that as a thrown Error so callers can show an inline banner.
   */
  approveExpense: async (id: number) => {
    try {
      const res = await api.put(`/finance/expenses/${id}/approve`);
      const updated: Expense = res.data;
      set((state) => ({
        expenses: state.expenses.map((e) => (e.id === id ? updated : e)),
        currentExpense: state.currentExpense?.id === id ? updated : state.currentExpense,
        error: null,
      }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to approve expense';
      set({ error: msg });
      throw new Error(msg);
    }
  },

  /**
   * PUT /api/finance/expenses/:id/reject   body: { reason }
   * Backend rejects empty/whitespace reason with 400.
   */
  rejectExpense: async (id: number, reason: string) => {
    try {
      const res = await api.put(`/finance/expenses/${id}/reject`, { reason });
      const updated: Expense = res.data;
      set((state) => ({
        expenses: state.expenses.map((e) => (e.id === id ? updated : e)),
        currentExpense: state.currentExpense?.id === id ? updated : state.currentExpense,
        error: null,
      }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to reject expense';
      set({ error: msg });
      throw new Error(msg);
    }
  },

  /**
   * GET /api/finance/expenses/summary
   * Used by FinanceDashboard and the Expenses page to render the
   * "Approval queue" tile (B10).
   */
  fetchExpenseSummary: async () => {
    try {
      const res = await api.get('/finance/expenses/summary');
      set({ expenseSummary: res.data, error: null });
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to load expense summary';
      set({ error: msg });
      console.error('Failed to fetch expense summary', err);
    }
  },

  // ============ §10a — Operational core ============

  // ---- Suppliers ----
  fetchSuppliers: async (params) => {
    try {
      const qs = new URLSearchParams();
      if (params?.search) qs.set('search', params.search);
      if (params?.status) qs.set('status', params.status);
      const res = await api.get(`/finance/suppliers?${qs.toString()}`);
      set({ suppliers: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load suppliers' });
      console.error('Failed to fetch suppliers', err);
    }
  },
  createSupplier: async (data) => {
    try {
      const res = await api.post('/finance/suppliers', data);
      const created: Supplier = res.data;
      set((state) => ({ suppliers: [created, ...state.suppliers], error: null }));
      return created;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create supplier';
      set({ error: msg });
      return null;
    }
  },
  updateSupplier: async (uid, data) => {
    try {
      const res = await api.patch(`/finance/suppliers/${uid}`, data);
      const updated: Supplier = res.data;
      set((state) => ({ suppliers: state.suppliers.map((s) => (s.uid === uid ? updated : s)), error: null }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update supplier';
      set({ error: msg });
      return null;
    }
  },
  deleteSupplier: async (uid) => {
    try {
      await api.delete(`/finance/suppliers/${uid}`);
      set((state) => ({ suppliers: state.suppliers.filter((s) => s.uid !== uid), error: null }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete supplier' });
      return false;
    }
  },

  // ---- Chart of accounts ----
  fetchChartAccounts: async (params) => {
    try {
      const qs = new URLSearchParams();
      if (params?.flat) qs.set('flat', 'true');
      if (params?.type) qs.set('type', params.type);
      const res = await api.get(`/finance/chart-of-accounts?${qs.toString()}`);
      set({ chartAccounts: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load chart of accounts' });
      console.error('Failed to fetch chart of accounts', err);
    }
  },
  createChartAccount: async (data) => {
    try {
      const res = await api.post('/finance/chart-of-accounts', data);
      const created: ChartAccount = res.data;
      set((state) => ({ chartAccounts: [created, ...state.chartAccounts], error: null }));
      return created;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create chart account';
      set({ error: msg });
      return null;
    }
  },
  updateChartAccount: async (uid, data) => {
    try {
      const res = await api.patch(`/finance/chart-of-accounts/${uid}`, data);
      const updated: ChartAccount = res.data;
      set((state) => ({ chartAccounts: state.chartAccounts.map((c) => (c.uid === uid ? updated : c)), error: null }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update chart account';
      set({ error: msg });
      return null;
    }
  },
  deleteChartAccount: async (uid) => {
    try {
      await api.delete(`/finance/chart-of-accounts/${uid}`);
      set((state) => ({ chartAccounts: state.chartAccounts.filter((c) => c.uid !== uid), error: null }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete chart account' });
      return false;
    }
  },

  // ---- Bank accounts ----
  fetchBankAccounts: async () => {
    try {
      const res = await api.get('/finance/banking/accounts');
      set({ bankAccounts: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load bank accounts' });
      console.error('Failed to fetch bank accounts', err);
    }
  },
  createBankAccount: async (data) => {
    try {
      const res = await api.post('/finance/banking/accounts', data);
      const created: BankAccount = res.data;
      set((state) => ({ bankAccounts: [created, ...state.bankAccounts], error: null }));
      return created;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create bank account';
      set({ error: msg });
      return null;
    }
  },
  updateBankAccount: async (uid, data) => {
    try {
      const res = await api.patch(`/finance/banking/accounts/${uid}`, data);
      const updated: BankAccount = res.data;
      set((state) => ({ bankAccounts: state.bankAccounts.map((b) => (b.uid === uid ? updated : b)), error: null }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update bank account';
      set({ error: msg });
      return null;
    }
  },
  deleteBankAccount: async (uid) => {
    try {
      await api.delete(`/finance/banking/accounts/${uid}`);
      set((state) => ({ bankAccounts: state.bankAccounts.filter((b) => b.uid !== uid), error: null }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete bank account' });
      return false;
    }
  },

  // ---- Reconciliations ----
  fetchReconciliations: async (bank_account_uid) => {
    try {
      const qs = bank_account_uid ? `?bank_account_uid=${encodeURIComponent(bank_account_uid)}` : '';
      const res = await api.get(`/finance/banking/reconciliations${qs}`);
      set({ reconciliations: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load reconciliations' });
      console.error('Failed to fetch reconciliations', err);
    }
  },
  fetchReconciliation: async (uid) => {
    try {
      const res = await api.get(`/finance/banking/reconciliations/${uid}`);
      return res.data as Reconciliation;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load reconciliation' });
      return null;
    }
  },
  reconcileBank: async (data) => {
    try {
      const res = await api.post('/finance/banking/reconcile', data);
      return { uid: res.data.uid, matched: res.data.matched, unmatched: res.data.unmatched };
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to reconcile' });
      return null;
    }
  },
  resolveReconciliationLine: async (recon_uid, line_id, action, payment_id) => {
    try {
      const body: any = { line_id, action };
      if (action === 'attach') body.payment_id = payment_id;
      const res = await api.post(`/finance/banking/reconciliations/${recon_uid}/resolve`, body);
      return res.data;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to resolve line' });
      return null;
    }
  },

  // ---- Invoices ----
  fetchInvoices: async (params) => {
    try {
      const qs = new URLSearchParams();
      if (params?.status) qs.set('status', params.status);
      if (params?.student_uid) qs.set('student_uid', params.student_uid);
      if (params?.fee_group_id) qs.set('fee_group_id', String(params.fee_group_id));
      if (params?.period_label) qs.set('period_label', params.period_label);
      if (params?.search) qs.set('search', params.search);
      qs.set('limit', '200');
      qs.set('offset', '0');
      const res = await api.get(`/finance/invoices?${qs.toString()}`);
      set({ invoices: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load invoices' });
      console.error('Failed to fetch invoices', err);
    }
  },
  fetchInvoice: async (uid) => {
    try {
      const res = await api.get(`/finance/invoices/${uid}`);
      set({ currentInvoice: res.data, error: null });
      return res.data as Invoice;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load invoice' });
      return null;
    }
  },
  createInvoice: async (data) => {
    try {
      const res = await api.post('/finance/invoices', data);
      const created: Invoice = res.data;
      set((state) => ({ invoices: [created, ...state.invoices], currentInvoice: created, error: null }));
      return created;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create invoice';
      set({ error: msg });
      return null;
    }
  },
  updateInvoice: async (uid, data) => {
    try {
      const res = await api.patch(`/finance/invoices/${uid}`, data);
      const updated: Invoice = res.data;
      set((state) => ({
        invoices: state.invoices.map((i) => (i.uid === uid ? updated : i)),
        currentInvoice: state.currentInvoice?.uid === uid ? updated : state.currentInvoice,
        error: null,
      }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update invoice';
      set({ error: msg });
      return null;
    }
  },
  deleteInvoice: async (uid) => {
    try {
      await api.delete(`/finance/invoices/${uid}`);
      set((state) => ({
        invoices: state.invoices.filter((i) => i.uid !== uid),
        currentInvoice: state.currentInvoice?.uid === uid ? null : state.currentInvoice,
        error: null,
      }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete invoice' });
      return false;
    }
  },
  sendInvoice: async (uid) => {
    try {
      await api.post(`/finance/invoices/${uid}/send`);
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to send invoice' });
      return false;
    }
  },

  // ---- Trips ----
  fetchTrips: async (params) => {
    try {
      const qs = new URLSearchParams();
      if (params?.status) qs.set('status', params.status);
      const res = await api.get(`/finance/trips?${qs.toString()}`);
      set({ trips: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load trips' });
      console.error('Failed to fetch trips', err);
    }
  },
  fetchTrip: async (uid) => {
    try {
      const res = await api.get(`/finance/trips/${uid}`);
      set({ currentTrip: res.data, error: null });
      return res.data as TripDetail;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load trip' });
      return null;
    }
  },
  createTrip: async (data) => {
    try {
      const res = await api.post('/finance/trips', data);
      const created: Trip = res.data;
      set((state) => ({ trips: [created, ...state.trips], error: null }));
      return created;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create trip';
      set({ error: msg });
      return null;
    }
  },
  updateTrip: async (uid, data) => {
    try {
      const res = await api.patch(`/finance/trips/${uid}`, data);
      const updated: Trip = res.data;
      set((state) => ({ trips: state.trips.map((t) => (t.uid === uid ? updated : t)), error: null }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update trip';
      set({ error: msg });
      return null;
    }
  },
  deleteTrip: async (uid) => {
    try {
      await api.delete(`/finance/trips/${uid}`);
      set((state) => ({ trips: state.trips.filter((t) => t.uid !== uid), error: null }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete trip' });
      return false;
    }
  },
  assignTripStudents: async (trip_uid, body) => {
    try {
      const res = await api.post(`/finance/trips/${trip_uid}/students`, body);
      return res.data;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to assign students' });
      return null;
    }
  },
  payTripStudent: async (trip_uid, student_uid, body) => {
    try {
      const res = await api.post(`/finance/trips/${trip_uid}/students/${student_uid}/pay`, body);
      return res.data;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to record payment' });
      return null;
    }
  },

  // ---- Fee plans ----
  fetchPlans: async (params) => {
    try {
      const qs = new URLSearchParams();
      if (params?.fee_group_id) qs.set('fee_group_id', String(params.fee_group_id));
      if (params?.academic_year) qs.set('academic_year', params.academic_year);
      const res = await api.get(`/fees/plans?${qs.toString()}`);
      set({ plans: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load fee plans' });
      console.error('Failed to fetch plans', err);
    }
  },
  fetchPlan: async (uid) => {
    try {
      const res = await api.get(`/fees/plans/${uid}`);
      set({ currentPlan: res.data, error: null });
      return res.data as FeePlan;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load plan' });
      return null;
    }
  },
  createPlan: async (data) => {
    try {
      const res = await api.post('/fees/plans', data);
      set((state) => ({ plans: [...state.plans, res.data], error: null }));
      return res.data;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create plan';
      set({ error: msg });
      return null;
    }
  },
  updatePlan: async (uid, data) => {
    try {
      const res = await api.patch(`/fees/plans/${uid}`, data);
      const updated: FeePlan = res.data;
      set((state) => ({ plans: state.plans.map((p) => (p.uid === uid ? updated : p)), error: null }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update plan';
      set({ error: msg });
      return null;
    }
  },
  deletePlan: async (uid) => {
    try {
      await api.delete(`/fees/plans/${uid}`);
      set((state) => ({ plans: state.plans.filter((p) => p.uid !== uid), error: null }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete plan' });
      return false;
    }
  },
  replacePlanInstallments: async (uid, installments) => {
    try {
      await api.put(`/fees/plans/${uid}/installments`, { installments });
      const refreshed = await api.get(`/fees/plans/${uid}`);
      set({ currentPlan: refreshed.data, error: null });
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to update installments' });
      return false;
    }
  },

  // ---- Discounts ----
  fetchDiscounts: async (params) => {
    try {
      const qs = new URLSearchParams();
      if (params?.scope) qs.set('scope', params.scope);
      if (params?.scope_id !== undefined) qs.set('scope_id', String(params.scope_id));
      if (params?.academic_year) qs.set('academic_year', params.academic_year);
      const res = await api.get(`/fees/discounts?${qs.toString()}`);
      set({ discounts: res.data || [], error: null });
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load discounts' });
      console.error('Failed to fetch discounts', err);
    }
  },
  fetchDiscount: async (uid) => {
    try {
      const res = await api.get(`/fees/discounts/${uid}`);
      return res.data as Discount;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to load discount' });
      return null;
    }
  },
  createDiscount: async (data) => {
    try {
      const res = await api.post('/fees/discounts', data);
      const created: Discount = res.data;
      set((state) => ({ discounts: [created, ...state.discounts], error: null }));
      return created;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to create discount';
      set({ error: msg });
      return null;
    }
  },
  updateDiscount: async (uid, data) => {
    try {
      const res = await api.patch(`/fees/discounts/${uid}`, data);
      const updated: Discount = res.data;
      set((state) => ({ discounts: state.discounts.map((d) => (d.uid === uid ? updated : d)), error: null }));
      return updated;
    } catch (err: any) {
      const msg = err?.response?.data?.error || err?.message || 'Failed to update discount';
      set({ error: msg });
      return null;
    }
  },
  deleteDiscount: async (uid) => {
    try {
      await api.delete(`/fees/discounts/${uid}`);
      set((state) => ({ discounts: state.discounts.filter((d) => d.uid !== uid), error: null }));
      return true;
    } catch (err: any) {
      set({ error: err?.response?.data?.error || err?.message || 'Failed to delete discount' });
      return false;
    }
  },
  fetchFeeGroups: async () => {
    try {
      const res = await api.get('/fees/groups');
      set({ feeGroups: res.data || [] });
    } catch (err: any) {
      console.error('Failed to fetch fee groups:', err);
    }
  },
}));

export default useFinanceStore;
