import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { formatUSD } from './money';

export function cn(...inputs: ClassValue[]): string {
  return twMerge(clsx(inputs));
}

// Canonical dollar formatter for the app ("$1,234.50"). Kept here for the many
// existing call sites; the implementation lives in ./money (formatUSD) so all
// currency formatting has a single source of truth. Inherits formatUSD's typed
// (branded-dollars) input.
export function fmt(amount: Parameters<typeof formatUSD>[0]): string {
  return formatUSD(amount);
}

// Narrow an unknown error (strict catch clauses give `unknown`) to a message
// string, falling back when the thrown value isn't an Error with a message.
export function errMessage(err: unknown, fallback = 'Something went wrong'): string {
  return err instanceof Error && err.message ? err.message : fallback;
}

export function fmtDate(dateStr: string | null | undefined): string {
  if (!dateStr) return '—';
  const [y = '', m = '', d = ''] = dateStr.split('-');
  return `${parseInt(m)}/${parseInt(d)}/${y}`;
}

export function localDateString(date: Date = new Date()): string {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year}-${month}-${day}`;
}

export function todayStr(): string {
  return localDateString();
}

export function fmtUptime(seconds: number): string {
  const d = Math.floor(seconds / 86400);
  const h = Math.floor((seconds % 86400) / 3600);
  const m = Math.floor((seconds % 3600) / 60);
  const s = seconds % 60;
  if (d > 0) return `${d}d ${h}h ${m}m`;
  if (h > 0) return `${h}h ${m}m ${s}s`;
  if (m > 0) return `${m}m ${s}s`;
  return `${s}s`;
}

export function fmtBytes(bytes: number | null | undefined): string {
  if (!bytes) return '0 B';
  if (bytes < 1024)    return `${bytes} B`;
  if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / 1048576).toFixed(2)} MB`;
}

interface CategoryTone {
  border: string;
  bg: string;
  text: string;
  bar: string;
}

const CATEGORY_COLOR_TONES: CategoryTone[] = [
  { border: 'border-emerald-300/50', bg: 'bg-emerald-400/15', text: 'text-emerald-700 dark:text-emerald-200', bar: 'bg-emerald-500' },
  { border: 'border-sky-300/50',     bg: 'bg-sky-400/15',     text: 'text-sky-700 dark:text-sky-200',     bar: 'bg-sky-500' },
  { border: 'border-amber-300/50',   bg: 'bg-amber-400/15',   text: 'text-amber-700 dark:text-amber-200',   bar: 'bg-amber-500' },
  { border: 'border-rose-300/50',    bg: 'bg-rose-400/15',    text: 'text-rose-700 dark:text-rose-200',    bar: 'bg-rose-500' },
  { border: 'border-indigo-300/50',  bg: 'bg-indigo-400/15',  text: 'text-indigo-700 dark:text-indigo-200',  bar: 'bg-indigo-500' },
  { border: 'border-violet-300/50',  bg: 'bg-violet-400/15',  text: 'text-violet-700 dark:text-violet-200',  bar: 'bg-violet-500' },
  { border: 'border-teal-300/50',    bg: 'bg-teal-400/15',    text: 'text-teal-700 dark:text-teal-200',    bar: 'bg-teal-500' },
  { border: 'border-orange-300/50',  bg: 'bg-orange-400/15',  text: 'text-orange-700 dark:text-orange-200',  bar: 'bg-orange-500' },
];

// Deterministic color tone for a category (or merchant) name, used for
// badges and avatars so the same name always renders the same color.
export function categoryColor(name: string | null | undefined): CategoryTone {
  const key = String(name || 'Uncategorized');
  let hash = 0;
  for (let i = 0; i < key.length; i++) {
    hash = (hash * 31 + key.charCodeAt(i)) | 0;
  }
  const index = Math.abs(hash) % CATEGORY_COLOR_TONES.length;
  return CATEGORY_COLOR_TONES[index]!; // index is always in range
}
