// Domain types for the API responses. Money fields are branded `Dollars` — the
// server stores integer cents and serializes dollars over the wire (via
// utils/money.fromCents), so every amount a component receives is dollars. Typing
// them `Dollars` lets `fmt`/`formatUSD` accept them while rejecting raw cents
// (e.g. bank-transaction `*_cents` fields), catching the cents-as-dollars bug
// class at compile time.
//
// Shapes are typed incrementally (endpoint-by-endpoint). Complex nested payloads
// that no typed consumer reads yet (trend series, autopay suggestion/stats,
// sparkline) are left `unknown` and refined when a consumer needs them.
import type { Cents, Dollars } from '@/lib/money';

// The authenticated user (GET /auth/me → `user`). Re-exported from
// `@/hooks/useAuth` for the many components that import it from there.
export interface User {
  id: number;
  username?: string;
  role?: string;
  display_name?: string;
  displayName?: string;
  name?: string;
  is_default_admin?: boolean;
  [key: string]: unknown;
}

// A bank transaction as serialized by the server. Unlike bill/payment amounts,
// raw bank-transaction amounts stay in CENTS on the wire — hence branded `Cents`
// (format them with formatCentsUSD, never formatUSD).
export interface BankTransaction {
  id: number;
  amount: Cents;
  currency?: string | null;
  payee?: string | null;
  description?: string | null;
  memo?: string | null;
  posted_date?: string | null;
  transacted_at?: string | number | null;
  account_name?: string | null;
  source_label?: string | null;
  source_type_label?: string | null;
  [key: string]: unknown;
}

// The bank ledger (GET /transactions/bank-ledger): accounts, the transaction
// occurrence with spending/match metadata, and the month summary. Raw
// transaction/account amounts stay in CENTS on this endpoint (the page formats
// them with a local formatCents), so they're plain `number` cents, not Dollars.
export interface BankLedgerTransaction extends BankTransaction {
  transacted_at?: string | null; // narrower than BankTransaction's for date slicing
  category?: string | null;
  pending?: boolean;
  ignored?: boolean;
  match_status?: string | null;
  matched_bill_name?: string | null;
  spending_category_id?: number | null;
  spending_category_name?: string | null;
  suggested_match?: { display_name?: string; category?: string } | null;
  account_org_name?: string | null;
  account_type?: string | null;
}

export interface BankAccount {
  id: number | string;
  org_name?: string;
  name?: string;
  account_type?: string;
  currency?: string;
  balance?: number;
  available_balance?: number | null;
  transaction_count?: number;
  last_transaction_date?: string;
}

export interface BankCategoryBreakdown { name: string; total?: number; count?: number }

export interface BankLedgerSummary {
  inflow?: number;
  outflow?: number;
  net?: number;
  matched?: number;
  unmatched?: number;
  pending?: number;
  total?: number;
  latest_date?: string;
  category_breakdown?: BankCategoryBreakdown[];
}

export interface BankLedger {
  accounts?: BankAccount[];
  transactions?: BankLedgerTransaction[];
  summary?: BankLedgerSummary;
  total?: number;
  sources?: { last_sync_at?: string | null }[];
  enabled?: boolean;
  has_connections?: boolean;
}

export interface AutoCategorizeChange { transaction_id: number }
export interface AutoCategorizePreview {
  changes?: AutoCategorizeChange[];
  categories?: { name: string; count?: number }[];
}

// The spending page (GET /spending/*). Amounts are dollars (server fromCents);
// kept as plain `number` because the page works in numbers via a local formatter.
export interface SpendingCategoryEntry {
  category_id: number | 'other' | null;
  category_name?: string;
  amount: number;
  budget?: number | null;
  tx_count?: number;
  avg_3mo?: number;
  group_id?: number | null;
}
export interface SpendingSummary {
  by_category?: SpendingCategoryEntry[];
  total_spending?: number;
  income?: number;
  uncategorized_amount?: number;
  uncategorized_count?: number;
}
export interface SpendingTransaction {
  id: number;
  payee?: string | null;
  date?: string | null;
  amount: number;
  spending_category_id?: number | string | null;
  spending_category_name?: string | null;
  [key: string]: unknown;
}
export interface SpendingTransactionsResponse { transactions?: SpendingTransaction[]; total?: number; pages?: number }
export interface SpendingIncomeTx { id: number; payee?: string | null; date?: string | null; amount: number }
export interface SpendingIncomeResponse { transactions?: SpendingIncomeTx[]; total?: number; pages?: number }
export interface SpendingRule { id: number; merchant: string; category_name?: string; category_id?: number }
export interface CategoryGroup { id: number; name: string }
export interface CopyBudgetsResponse { copied: number; budgets?: { category_id: number; amount: number }[] }

// The subscriptions page (GET /subscriptions, /subscriptions/recommendations,
// /subscriptions/transaction-matches). A Subscription is a decorated Bill;
// amounts here are dollars kept as plain `number` (the page uses a local
// formatter), except SubTx.amount which is a raw bank amount in CENTS.
export interface Subscription {
  id: number;
  name: string;
  active?: boolean | number;
  is_subscription?: boolean | number;
  category_id?: number | null;
  category_name?: string | null;
  expected_amount?: number | null;
  monthly_equivalent?: number | null;
  yearly_equivalent?: number | null;
  next_due_date?: string | null;
  subscription_type?: string;
  due_day?: number | null;
  cycle_type?: string | null;
  billing_cycle?: string | null;
  cycle_day?: number | string | null;
  autopay_enabled?: unknown;
  has_2fa?: unknown;
  has_merchant_rule?: unknown;
  has_linked_transactions?: unknown;
  reminder_days_before?: number | null;
  [key: string]: unknown;
}
export interface SubscriptionTopType { type: string; monthly_total: number }
export interface SubscriptionSummary {
  active_count?: number;
  paused_count?: number;
  monthly_total?: number;
  yearly_total?: number;
  top_type?: SubscriptionTopType | null;
}
export interface SubscriptionsResponse { summary?: SubscriptionSummary; subscriptions?: Subscription[] }

export interface CatalogMatch {
  id?: number | string;
  name: string;
  subscription_type?: string;
  website?: string;
  starting_monthly_usd?: number | null;
  starting_annual_usd?: number | null;
}
export interface ExistingBillMatch {
  id: number;
  name: string;
  expected_amount?: number | null;
  due_day?: number | null;
  reasons?: string[];
}
export interface RecommendationEvidence {
  identity?: { label?: string };
  amount?: { label?: string; match?: string };
  cadence?: { label?: string; recurring?: boolean };
  amount_range?: { min: number; max: number };
  ambiguity?: { ambiguous?: boolean; label?: string; reasons?: string[] };
}
export interface RecommendationTransaction { id: number; payee?: string; description?: string; memo?: string; date?: string; account?: string; amount?: number }
export interface Recommendation {
  id: number | string;
  name: string;
  subscription_type?: string;
  occurrence_count?: number;
  last_seen_date?: string;
  expected_amount?: number | null;
  monthly_equivalent?: number | null;
  confidence?: number;
  cycle_type?: string;
  merchant?: string;
  decline_key?: string;
  transaction_ids?: (number | string)[];
  accounts?: string[];
  reasons?: string[];
  evidence?: RecommendationEvidence;
  existing_bill_match?: ExistingBillMatch | null;
  catalog_match?: CatalogMatch | null;
  transactions?: RecommendationTransaction[];
  [key: string]: unknown;
}
// The snowball projection (GET /snowball/projection) and settings
// (PATCH /snowball/settings). Amounts are dollars kept as plain `number`.
export interface SnowballDebtProjection {
  id: number;
  name: string;
  payoff_display?: string;
  months?: number;
  total_interest?: number;
  current_balance?: number;
}
export interface SnowballProjectionDetail {
  debts: SnowballDebtProjection[];
  skipped: { name: string }[];
  payoff_display?: string;
  months_to_freedom?: number;
  total_interest_paid?: number;
  capped?: boolean;
}
export interface AvalancheProjectionDetail {
  months_to_freedom?: number;
  total_interest_paid?: number;
  payoff_display?: string;
}
export interface SnowballProjection {
  snowball?: SnowballProjectionDetail;
  avalanche?: AvalancheProjectionDetail;
}
export interface SnowballSettings {
  extra_payment?: number;
  ramsey_mode?: boolean;
  ready_current_on_bills?: boolean;
  ready_emergency_fund?: boolean;
}

// A raw bank transaction on the subscription-match search — amount in CENTS.
export interface SubscriptionTx {
  id: number;
  amount: number;
  payee?: string;
  description?: string;
  memo?: string;
  account_name?: string;
  data_source_name?: string;
  account?: string;
  match_status?: string;
  matched_bill_name?: string;
  catalog_match?: CatalogMatch | null;
  posted_date?: string;
  date?: string;
  [key: string]: unknown;
}

// A bill's month status. Mirrors the server's statusService.
export type TrackerStatus =
  | 'paid' | 'autodraft' | 'upcoming' | 'due_soon' | 'late' | 'missed' | 'skipped';

// A price-drift finding (driftService.getDriftReport) — a bill whose recent
// payments trend away from its expected amount.
export interface DriftBill {
  id: number;
  name: string;
  category_name?: string | null;
  expected_amount: Dollars;
  recent_amount: Dollars;
  direction: string;
  drift_pct: number;
  months_sampled: number;
}

// A suggested autopay payment (trackerService.applyAutopaySuggestions).
export interface AutopaySuggestion {
  bill_id: number;
  amount: Dollars;
  paid_date: string;
  method: string;
}

// A suggested expected-amount for a bill, from its recent payment history.
export interface AmountSuggestion {
  suggestion: Dollars;
  months_used: number;
}

// Autopay reliability rollup shown in the tracker-row tooltip.
export interface AutopayStats {
  total: number;
  failures: number;
  last_failure_date?: string | null;
  last_failure_notes?: string | null;
}

// A payment record as serialized by the server (money in dollars). serializePayment
// spreads the full DB row, so extra columns pass through as `unknown`.
export interface Payment {
  id: number;
  bill_id?: number;
  amount: Dollars;
  paid_date: string;
  method?: string | null;
  payment_source?: string | null;
  notes?: string | null;
  autopay_failure?: number | null;
  balance_delta?: Dollars | null;
  interest_delta?: Dollars | null;
  created_at?: string;
  deleted_at?: string | null;
  [key: string]: unknown;
}

// A category's per-bill rollup (categories list endpoint). `total_paid` here is a
// raw SQL sum (not run through fromCents), so it is deliberately NOT branded.
export interface CategoryBillSummary {
  id: number;
  name: string;
  active: boolean;
  payment_count: number;
  total_paid: number;
  last_paid_date: string | null;
  [key: string]: unknown;
}

// A spending/bill category. Has no money field of its own (budgets live on the
// spending endpoints). The list endpoint adds bill rollups; create/update return
// the bare row — so the rollup fields are optional.
export interface Category {
  id: number;
  user_id?: number;
  name: string;
  sort_order?: number;
  spending_enabled: boolean;
  group_id: number | null;
  created_at?: string;
  updated_at?: string;
  bill_count?: number;
  active_bill_count?: number;
  inactive_bill_count?: number;
  payment_count?: number;
  bill_names?: string[];
  bills?: CategoryBillSummary[];
  [key: string]: unknown;
}

// A bill as serialized by the server (billsService.serializeBill spreads the DB
// row and converts the three money columns to dollars). SQLite booleans come back
// as 0/1 integers. Extra joined columns (sparkline, has_merchant_rule, …) pass
// through as `unknown` via the index signature.
export interface Bill {
  id: number;
  user_id?: number;
  name: string;
  category_id: number | null;
  category_name?: string | null;
  due_day: number | null;
  override_due_date: string | null;
  bucket: string;
  expected_amount: Dollars;
  interest_rate: number | null;
  billing_cycle: string | null;
  autopay_enabled: number;
  autodraft_status: string | null;
  auto_mark_paid: number;
  website: string | null;
  username: string | null;
  account_info: string | null;
  has_2fa: number;
  notes: string | null;
  history_visibility: string | null;
  active: number;
  cycle_type: string | null;
  cycle_day: string | number | null;
  current_balance: Dollars | null;
  minimum_payment: Dollars | null;
  snowball_order: number | null;
  snowball_include: number;
  snowball_exempt: number;
  is_subscription: number;
  subscription_type: string | null;
  reminder_days_before: number | null;
  subscription_source: string | null;
  subscription_detected_at: string | null;
  created_at?: string;
  updated_at?: string;
  deleted_at?: string | null;
  [key: string]: unknown;
}

// One tracker row: a bill's state for a given month. Built by
// statusService.buildTrackerRow and augmented by trackerService.getTracker.
export interface TrackerRow {
  id: number;
  name: string;
  category_id: number | null;
  category_name: string | null;
  due_date: string;
  due_day: number | null;
  bucket: string;
  expected_amount: Dollars;
  notes: string | null;
  total_paid: Dollars;
  paid_toward_due: Dollars;
  overpaid_amount: Dollars;
  balance: Dollars;
  has_payment: boolean;
  is_settled: boolean;
  last_paid_date: string | null;
  last_payment_amount: Dollars | null;
  status: TrackerStatus;
  autopay_enabled: boolean;
  autodraft_status: string | null;
  auto_mark_paid: boolean;
  billing_cycle: string | null;
  cycle_type: string | null;
  cycle_day: string | number | null;
  current_balance: Dollars | null;
  minimum_payment: Dollars | null;
  interest_rate: number | null;
  is_subscription: boolean;
  has_2fa: boolean;
  has_merchant_rule: boolean;
  has_linked_transactions: boolean;
  website: string | null;
  autopay_verified_at: string | null;
  inactive_reason: string | null;
  inactivated_at: string | null;
  sparkline: number[] | null;
  autopay_stats: AutopayStats | null;
  payments: Payment[];
  // Augmented by getTracker:
  actual_amount: Dollars | null;
  is_skipped: boolean;
  snoozed_until: string | null;
  monthly_notes?: string | null;
  autopay_suggestion?: AutopaySuggestion | null;
  amount_suggestion?: AmountSuggestion | null;
  previous_month_paid: Dollars;
  // Bank-mode augmentation (present only when bank_tracking.enabled):
  pending_cleared?: boolean;
  bank_pending_count?: number;
}

export interface TrackerSummary {
  total_expected: Dollars;
  total_starting: Dollars;
  has_starting_amounts: boolean;
  total_paid: Dollars;
  paid_toward_due: Dollars;
  remaining: Dollars;
  total_remaining: Dollars;
  remaining_period: string;
  remaining_label: string;
  remaining_hint: string;
  overdue: Dollars;
  count_paid: number;
  count_upcoming: number;
  count_late: number;
  count_autodraft: number;
  previous_month_total: Dollars;
  trend: unknown;
}

// Bank tracking is off (`{ enabled: false }`) or on with the account snapshot.
export type BankTracking =
  | { enabled: false }
  | {
      enabled: true;
      account_id: number;
      account_name: string;
      org_name: string;
      balance: Dollars;
      pending_payments: Dollars;
      pending_days: number;
      effective_balance: Dollars;
      unpaid_this_month: Dollars;
      remaining: Dollars;
      last_updated: string | null;
    };

export interface SafeToSpendUpcoming {
  id: number;
  name: string;
  due_date: string;
  amount: Dollars;
  status: TrackerStatus;
}

// A bill landing on a given cashflow-timeline day.
export interface TimelineBill {
  name: string;
  amount: Dollars;
}

export interface CashflowTimelinePoint {
  date: string;
  balance: number; // running dollar balance; cashflowUtils treats it loosely
  bills: TimelineBill[];
  payday?: boolean;
}

export interface TrackerCashflow {
  next_payday: string | null;
  days_until_payday: number;
  available: Dollars;
  safe_to_spend: Dollars;
  still_due_total: Dollars;
  still_due_count: number;
  upcoming: SafeToSpendUpcoming[];
  timeline: CashflowTimelinePoint[];
  has_data: boolean;
  uses_bank_balance: boolean;
  period: string;
  period_end_label: string;
  period_starting: Dollars;
  period_bills_total: Dollars;
  period_paid: Dollars;
  period_paid_count: number;
  period_total_count: number;
  period_projected: Dollars;
  month_starting: Dollars;
  month_bills_total: Dollars;
  month_paid: Dollars;
  month_paid_count: number;
  month_total_count: number;
  month_projected: Dollars;
}

export interface TrackerResponse {
  year: number;
  month: number;
  today: string;
  summary: TrackerSummary;
  bank_tracking: BankTracking;
  cashflow: TrackerCashflow;
  rows: TrackerRow[];
}
