// Spanish label maps + formatting helpers. Single source of truth for i18n.

import type {
  BankDirection,
  LedgerDirection,
  PolicyStatus,
  Role,
  ServiceKind,
  TransactionDomain,
  TrustStatus,
} from "./types";

/** Access tiers, high → low. VIEWER is read-only; STAFF+ can write. */
export const ROLE_LABEL: Record<Role, string> = {
  ADMIN: "Administrador",
  MANAGER: "Gerente",
  STAFF: "Personal",
  VIEWER: "Solo lectura",
};

/** Roles in descending rank — for populating role <select>s. */
export const ROLES_DESC: Role[] = ["ADMIN", "MANAGER", "STAFF", "VIEWER"];

/**
 * Placeholder the migration writes when a legacy record had no name and none
 * could be recovered from a secondary table (migration/transform_customers.py).
 */
export const SIN_NOMBRE = "(SIN NOMBRE)";

export const DOMAIN_LABELS: Record<string, string> = {
  UTILITY: "Servicios",
  INSURANCE: "Seguros",
  TRUST: "Fideicomiso",
};

export function domainLabel(domain: TransactionDomain): string {
  return DOMAIN_LABELS[domain] ?? domain;
}

export const SERVICE_KIND_LABELS: Record<string, string> = {
  WATER: "Agua",
  ELECTRIC: "Electricidad",
  GAS: "Gas",
  CABLE: "Cable/TV",
  PROPERTY_TAX: "Predial",
  FEDERAL_ZONE: "Zona Federal",
  ALARM: "Alarma",
  TELEPHONE: "Teléfono",
  OTHER: "Otro",
};

export function serviceKindLabel(kind: ServiceKind): string {
  return SERVICE_KIND_LABELS[kind] ?? kind;
}

// A short glyph per service kind, drawn with unicode so no icon dependency.
export const SERVICE_KIND_GLYPH: Record<string, string> = {
  WATER: "≈",
  ELECTRIC: "⚡",
  GAS: "◐",
  TELEPHONE: "☎",
  CABLE: "▤",
  PROPERTY_TAX: "⌂",
  FEDERAL_ZONE: "⇲",
  ALARM: "◈",
  OTHER: "•",
};

export function serviceKindGlyph(kind: ServiceKind): string {
  return SERVICE_KIND_GLYPH[kind] ?? "•";
}

/**
 * Free-text detail the migration parked in `PropertyService.notes`, which
 * means something different per service kind: the municipality that bills the
 * predial / zona federal, the CFE billing cycle (PAR/IMPAR), and the gas
 * supply type. Used to label the note instead of dumping a bare string.
 */
export const SERVICE_NOTE_LABELS: Record<string, string> = {
  PROPERTY_TAX: "Municipio",
  FEDERAL_ZONE: "Municipio",
  ELECTRIC: "Ciclo",
  GAS: "Suministro",
  CABLE: "Proveedor",
};

export function serviceNoteLabel(kind: ServiceKind): string | null {
  return SERVICE_NOTE_LABELS[kind] ?? null;
}

// ----- fideicomisos (trusts) -----

export const TRUST_STATUS_LABELS: Record<TrustStatus, string> = {
  active: "Vigente",
  expiring: "Por vencer",
  expired: "Vencido",
  undated: "Sin fecha",
};

export function trustStatusLabel(status: TrustStatus): string {
  return TRUST_STATUS_LABELS[status] ?? status;
}

// ----- policies -----

export const POLICY_STATUS_LABELS: Record<PolicyStatus, string> = {
  active: "Vigente",
  expiring: "Por vencer",
  expired: "Vencida",
  undated: "Sin vigencia",
};

export function policyStatusLabel(status: PolicyStatus): string {
  return POLICY_STATUS_LABELS[status] ?? status;
}

/**
 * Headline premium for a policy: always `netPremium`.
 *
 * The legacy `total` column did not survive the migration as a usable figure —
 * of 2378 policies only 2 carry a non-zero total (1585 are literally 0, 791
 * null), and one of those two is *lower* than its own net premium. `netPremium`
 * is populated on 2377 of 2378. `total` is still shown verbatim in the policy
 * detail's condiciones grid, where it reads as source data rather than as the
 * amount the customer owes.
 */
export function premiumHeadline(p: {
  netPremium?: string | null;
}): { value: string | null; label: string } {
  return { value: p.netPremium ?? null, label: "Prima neta" };
}

/** "vence en 12 días" / "venció hace 3 días" — null when the policy is undated. */
export function expiryPhrase(days: number | null): string | null {
  if (days === null) return null;
  if (days === 0) return "vence hoy";
  if (days > 0) return `vence en ${days} ${days === 1 ? "día" : "días"}`;
  const past = Math.abs(days);
  return `venció hace ${past} ${past === 1 ? "día" : "días"}`;
}

// ----- ledger / estado de cuenta -----

/**
 * A charge is negative and a credit positive (see `billing.service.ts`), so the
 * balance is the plain sum. These are the two words the office uses.
 */
export const DIRECTION_LABELS: Record<LedgerDirection, string> = {
  charge: "Cargo",
  credit: "Abono",
};

export function directionLabel(d: LedgerDirection): string {
  return DIRECTION_LABELS[d] ?? d;
}

/**
 * Spanish names for the legacy `TYPE OF TRX` lookup.
 *
 * The lookup ships an `ESPAÑOL` column, but it is **empty in the source** — all
 * 79 rows are null — so the API can only return the English name. This map
 * covers the entries that are real service/payment categories; the rest of the
 * 79 are payee names (LORETO GONZALEZ, ALBERCAS VALLARTA…) that shouldn't be
 * translated anyway, and fall through to the raw value.
 */
export const TX_TYPE_LABELS: Record<string, string> = {
  WATER: "Agua",
  ELECTRIC: "Electricidad",
  TELEPHONE: "Teléfono",
  "PROPERTY TAXES": "Predial",
  "FEDERAL ZONE": "Zona federal",
  "GAS BUTANO": "Gas butano",
  "GAS REFILL": "Recarga de gas",
  "TRUST FEE": "Cuota de fideicomiso",
  "HOA DUES": "Cuota de asociación",
  "ALARM SYSTEM": "Sistema de alarma",
  "HOUSE INSURANCE": "Seguro de casa",
  "AUTO INSURANCE": "Seguro de auto",
  "CHECK DEPOSIT": "Depósito con cheque",
  "CASH DEPOSIT": "Depósito en efectivo",
  PAYPAL: "PayPal",
  "RETURNED CHECK": "Cheque devuelto",
  "ACCOUNT CANCELED": "Cuenta cancelada",
  "BANK FEE": "Comisión bancaria",
  "BANK INTEREST": "Interés bancario",
  SECURITY: "Vigilancia",
  BALANCE: "Saldo",
  ACCOUNTANT: "Contador",
  "RENEWAL CONCESSION": "Renovación de concesión",
};

export function txTypeLabel(
  type: { nameEs?: string | null; nameEn?: string | null } | null | undefined,
): string {
  const raw = type?.nameEs || type?.nameEn;
  if (!raw) return "Sin clasificar";
  return TX_TYPE_LABELS[raw.toUpperCase()] ?? raw;
}

/**
 * Legacy table a movement came from. Shown so a staff member checking a
 * surprising figure can trace it back to the Access table it was migrated from.
 */
export const LEDGER_SOURCE_LABELS: Record<string, string> = {
  datos2: "Facturación 2025–26",
  "FEE ANUAL": "Cuota anual 2018",
  fee15: "Cuota anual 2017",
  "IVA 2015": "IVA 2015",
  EFECTIVO: "Recibos de caja",
  EFECTIVO_BACKUP: "Recibos de caja (respaldo)",
  "EFECTIVO FM3": "Trámites FM3",
  "CHEQUE FM3": "Trámites FM3 (cheque)",
};

export function ledgerSourceLabel(source: string | null | undefined): string {
  if (!source) return "—";
  return LEDGER_SOURCE_LABELS[source] ?? source;
}

/**
 * Balance wording. Negative = the customer owes the office; positive = the
 * customer is in credit (they have money on account).
 */
export function balancePhrase(balance: string | number): string {
  const n = typeof balance === "string" ? Number(balance) : balance;
  if (!Number.isFinite(n) || Math.abs(n) < 0.005) return "Sin saldo";
  return n < 0 ? "Adeudo" : "A favor";
}

/** CSS-class suffix matching `balancePhrase`, for colouring a figure. */
export function balanceTone(balance: string | number): "owing" | "credit" | "flat" {
  const n = typeof balance === "string" ? Number(balance) : balance;
  if (!Number.isFinite(n) || Math.abs(n) < 0.005) return "flat";
  return n < 0 ? "owing" : "credit";
}

// ----- chequera / bank register -----

/**
 * The office's own account, so the words are the bank's, not the ledger's:
 * an ingreso is money arriving, an egreso money leaving, and a zero-amount row
 * is a cheque that was voided.
 */
export const BANK_DIRECTION_LABELS: Record<BankDirection, string> = {
  income: "Ingreso",
  expense: "Egreso",
  void: "Cancelado",
};

export function bankDirectionLabel(d: BankDirection): string {
  return BANK_DIRECTION_LABELS[d] ?? d;
}

/** CSS-class suffix for colouring a bank figure, matching `.tx-amount`. */
export function bankTone(d: BankDirection): "pos" | "neg" | "" {
  if (d === "income") return "pos";
  return d === "expense" ? "neg" : "";
}

/** Legacy SCOTHIA table a register row came from. */
export const BANK_SOURCE_LABELS: Record<string, string> = {
  "DATOS I": "Ingresos",
  "DATOS E": "Egresos",
};

export function bankSourceLabel(source: string | null | undefined): string {
  if (!source) return "—";
  return BANK_SOURCE_LABELS[source] ?? source;
}

// ----- DB operations (admin) -----

export const OPS_KIND_LABELS: Record<string, string> = {
  BACKUP: "Respaldo",
  RESTORE: "Restauración",
  REIMPORT: "Reimportación",
  SYNC: "Sincronización",
};

export const OPS_STATUS_LABELS: Record<string, string> = {
  RUNNING: "En curso",
  SUCCESS: "Completado",
  FAILED: "Con error",
};

/** Bytes → human size (KB/MB/GB), es-MX formatting. */
export function formatBytes(bytes: number | null | undefined): string {
  if (bytes === null || bytes === undefined) return "—";
  if (bytes < 1024) return `${bytes} B`;
  const units = ["KB", "MB", "GB"];
  let n = bytes / 1024;
  let i = 0;
  while (n >= 1024 && i < units.length - 1) {
    n /= 1024;
    i++;
  }
  return `${n.toLocaleString("es-MX", { maximumFractionDigits: 1 })} ${units[i]}`;
}

export function formatDateTime(iso: string | null | undefined): string {
  if (!iso) return "—";
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return "—";
  return d.toLocaleString("es-MX", {
    day: "2-digit",
    month: "2-digit",
    year: "numeric",
    hour: "2-digit",
    minute: "2-digit",
  });
}

export const MONTH_NAMES = [
  "Enero",
  "Febrero",
  "Marzo",
  "Abril",
  "Mayo",
  "Junio",
  "Julio",
  "Agosto",
  "Septiembre",
  "Octubre",
  "Noviembre",
  "Diciembre",
];

export function monthName(month: number): string {
  return MONTH_NAMES[month - 1] ?? String(month);
}

// ----- formatting -----

export function formatMoney(
  value: string | number | null | undefined,
  currency: string | null | undefined,
): string {
  if (value === null || value === undefined || value === "") return "—";
  const num = typeof value === "string" ? Number(value) : value;
  if (Number.isNaN(num)) return String(value);
  const cur = (currency ?? "USD").toUpperCase();
  try {
    return new Intl.NumberFormat("es-MX", {
      style: "currency",
      currency: cur,
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    }).format(num);
  } catch {
    // Unknown currency code — fall back to plain number + suffix.
    return `${num.toLocaleString("es-MX", {
      minimumFractionDigits: 2,
      maximumFractionDigits: 2,
    })} ${cur}`;
  }
}

export function formatDate(
  iso: string | null | undefined,
): string {
  if (!iso) return "—";
  const d = new Date(iso);
  if (Number.isNaN(d.getTime())) return "—";
  const dd = String(d.getUTCDate()).padStart(2, "0");
  const mm = String(d.getUTCMonth() + 1).padStart(2, "0");
  const yyyy = d.getUTCFullYear();
  return `${dd}/${mm}/${yyyy}`;
}

export function formatNumber(n: number): string {
  return n.toLocaleString("es-MX");
}

// "sourceSystem" from legacyRefs → display label.
export function sourceSystemLabel(source: string): string {
  const map: Record<string, string> = {
    utilities: "Servicios",
    insurance: "Seguros",
  };
  return map[source] ?? source;
}

// ----- Mass email notifications -----

import type {
  NotificationStatus,
  NotificationType,
  NotificationServicio,
} from "./api";

export const NOTIFICATION_TYPE_LABELS: Record<NotificationType, string> = {
  OUTSTANDING_PAYMENT: "Pagos pendientes",
  PAYMENT_CONFIRMATION: "Confirmación de pago",
  ACCOUNT_STATUS: "Estado de cuenta",
  TRUST_PAYMENT_CONFIRMATION: "Confirmación fideicomiso",
  RENEWAL_NOTICE: "Aviso de renovación",
};

export const NOTIFICATION_SERVICIO_LABELS: Record<NotificationServicio, string> = {
  CUSTOMERS: "Clientes",
  TRUST: "Fideicomiso",
  POLICIES: "Pólizas",
};

/**
 * The `level` column means something different per notification type, so it
 * can only be read alongside one. ACCOUNT_STATUS uses it for the alert colour;
 * RENEWAL_NOTICE for the aviso generation. Everything else leaves it null.
 */
export function notificationLevelLabel(
  type: NotificationType,
  level: number | null,
): string {
  if (level === null) return "";
  if (type === "ACCOUNT_STATUS") return level === 0 ? " (amarilla)" : " (roja)";
  if (type === "RENEWAL_NOTICE") {
    if (level === 1) return " (1.º, 30 días antes)";
    if (level === 2) return " (2.º, 15 días antes)";
    if (level === 3) return " (3.º, 7 días después)";
    return ` (aviso ${level})`;
  }
  return "";
}

export const NOTIFICATION_STATUS_LABELS: Record<NotificationStatus, string> = {
  SENT: "Enviado",
  FAILED: "Falló",
  SKIPPED_NO_EMAIL: "Sin email",
  SKIPPED_GATE: "Fuera de día",
};

export const NOTIFICATION_STATUS_COLORS: Record<NotificationStatus, string> = {
  SENT: "var(--positive)",
  FAILED: "var(--negative)",
  SKIPPED_NO_EMAIL: "var(--muted)",
  SKIPPED_GATE: "var(--muted-2)",
};
