/**
 * Shared date/time formatting utilities for the renderer.
 *
 * All functions accept a `FormatPrefs` object so every render site honours
 * the user's chosen timezone, date format, and 12h/24h preference.
 *
 * Use `useFormatPrefs()` in components to get the prefs with a safe fallback.
 */

import { trpc } from './trpc';

export interface FormatPrefs {
  timezone: string;
  dateFormat: string; // 'dd/MM/yyyy' | 'MM/dd/yyyy' | 'yyyy-MM-dd'
  timeFormat: '12h' | '24h';
}

// ---------------------------------------------------------------------------
// Browser-side fallback detection (mirrors locale-defaults.ts on main side)
// ---------------------------------------------------------------------------

function inferDateFormat(locale: string): string {
  const mdyLocales = ['en-US', 'en-PH', 'en-BZ'];
  if (mdyLocales.some((l) => locale.startsWith(l))) return 'MM/dd/yyyy';
  const ymdPrefixes = ['ja', 'zh', 'ko', 'hu', 'lt', 'sv', 'fi'];
  if (ymdPrefixes.some((p) => locale.startsWith(p))) return 'yyyy-MM-dd';
  return 'dd/MM/yyyy';
}

function detectBrowserFormatPrefs(): FormatPrefs {
  const locale = navigator.language;
  const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
  const hour12 = Intl.DateTimeFormat(locale, { hour: 'numeric' }).resolvedOptions().hour12 ?? false;
  return {
    timezone,
    timeFormat: hour12 ? '12h' : '24h',
    dateFormat: inferDateFormat(locale),
  };
}

// ---------------------------------------------------------------------------
// React hook
// ---------------------------------------------------------------------------

/** Returns FormatPrefs, falling back to OS-detected values while loading. */
export function useFormatPrefs(): FormatPrefs {
  const { data } = trpc.settings.getFormatPrefs.useQuery();
  return data ?? detectBrowserFormatPrefs();
}

// ---------------------------------------------------------------------------
// Pure formatting functions (usable outside React)
// ---------------------------------------------------------------------------

/** Format the date portion of a ms-epoch timestamp per user prefs. */
export function formatDate(ms: number, prefs: FormatPrefs): string {
  const d = new Date(ms);
  const opts: Intl.DateTimeFormatOptions = { timeZone: prefs.timezone };

  const day = d.toLocaleDateString('en-CA', { ...opts, day: '2-digit' }).slice(-2);
  const month = d.toLocaleDateString('en-CA', { ...opts, month: '2-digit' }).slice(-2);
  const year = d.toLocaleDateString('en-CA', { ...opts, year: 'numeric' }).slice(0, 4);

  switch (prefs.dateFormat) {
    case 'MM/dd/yyyy': return `${month}/${day}/${year}`;
    case 'yyyy-MM-dd': return `${year}-${month}-${day}`;
    default:           return `${day}/${month}/${year}`;
  }
}

/** Format the time portion of a ms-epoch timestamp per user prefs. */
export function formatTime(ms: number, prefs: FormatPrefs): string {
  return new Date(ms).toLocaleTimeString('en-US', {
    timeZone: prefs.timezone,
    hour12: prefs.timeFormat === '12h',
    hour: '2-digit',
    minute: '2-digit',
  });
}

/** Format a ms-epoch timestamp as date + time per user prefs. */
export function formatDateTime(ms: number, prefs: FormatPrefs): string {
  return `${formatDate(ms, prefs)} ${formatTime(ms, prefs)}`;
}

/**
 * Format a ms-epoch timestamp as date (+ time if not midnight in user tz).
 * Used for task due dates where midnight means "no time set".
 */
export function formatDueDate(ms: number, prefs: FormatPrefs): string {
  const parts = new Intl.DateTimeFormat('en-CA', {
    timeZone: prefs.timezone,
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).formatToParts(new Date(ms));

  const hour = parseInt(parts.find((p) => p.type === 'hour')?.value ?? '0', 10);
  const minute = parseInt(parts.find((p) => p.type === 'minute')?.value ?? '0', 10);

  const now = new Date();
  const sameYear = new Date(ms).toLocaleDateString('en-CA', { timeZone: prefs.timezone, year: 'numeric' }).slice(0, 4)
    === now.toLocaleDateString('en-CA', { timeZone: prefs.timezone, year: 'numeric' }).slice(0, 4);

  // When same year, omit the year component for brevity
  const d = new Date(ms);
  const opts: Intl.DateTimeFormatOptions = { timeZone: prefs.timezone };
  const day = d.toLocaleDateString('en-CA', { ...opts, day: '2-digit' }).slice(-2);
  const month = d.toLocaleDateString('en-CA', { ...opts, month: '2-digit' }).slice(-2);
  const year = d.toLocaleDateString('en-CA', { ...opts, year: 'numeric' }).slice(0, 4);
  const yr2 = year.slice(2);

  let datePart: string;
  if (sameYear) {
    switch (prefs.dateFormat) {
      case 'MM/dd/yyyy': datePart = `${month}/${day}`; break;
      case 'yyyy-MM-dd': datePart = `${month}-${day}`; break;
      default:           datePart = `${day}/${month}`; break;
    }
  } else {
    switch (prefs.dateFormat) {
      case 'MM/dd/yyyy': datePart = `${month}/${day}/${yr2}`; break;
      case 'yyyy-MM-dd': datePart = `20${yr2}-${month}-${day}`; break;
      default:           datePart = `${day}/${month}/${yr2}`; break;
    }
  }

  if (hour === 0 && minute === 0) return datePart;
  return `${datePart} ${formatTime(ms, prefs)}`;
}

/** Returns true if the given ms-epoch timestamp is in the past. */
export function isOverdue(ts: number): boolean {
  return ts < Date.now();
}

/**
 * Relative time: "just now", "5m ago", "2h ago", "3d ago".
 * Intentionally kept simple — not i18n'd yet.
 */
export function formatRelative(ms: number): string {
  const diff = Date.now() - ms;
  const minutes = Math.floor(diff / 60_000);
  if (minutes < 1) return 'just now';
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  return `${Math.floor(hours / 24)}d ago`;
}

/**
 * Duration between two timestamps: "45s", "2m 30s", "5m".
 * Returns null if endMs is falsy (run still in progress).
 */
export function formatDuration(startMs: number, endMs: number | null | undefined): string | null {
  if (!endMs) return null;
  const startNorm = startMs < 1e12 ? startMs * 1000 : startMs;
  const endNorm = endMs < 1e12 ? endMs * 1000 : endMs;
  const diffSec = Math.round((endNorm - startNorm) / 1000);
  if (diffSec < 60) return `${diffSec}s`;
  const m = Math.floor(diffSec / 60);
  const s = diffSec % 60;
  return s > 0 ? `${m}m ${s}s` : `${m}m`;
}

/**
 * Format a ms-epoch timestamp for display in run logs and history sheets.
 * Shows month, day, hour, minute in user's timezone and format.
 */
export function formatTs(ms: number | null | undefined, prefs: FormatPrefs): string {
  if (!ms) return '—';
  const norm = ms < 1e12 ? ms * 1000 : ms;
  const d = new Date(norm);
  const opts: Intl.DateTimeFormatOptions = { timeZone: prefs.timezone };

  const day = d.toLocaleDateString('en-CA', { ...opts, day: '2-digit' }).slice(-2);
  const month = d.toLocaleDateString('en-CA', { ...opts, month: '2-digit' }).slice(-2);
  const time = d.toLocaleTimeString('en-US', {
    ...opts,
    hour12: prefs.timeFormat === '12h',
    hour: '2-digit',
    minute: '2-digit',
  });

  switch (prefs.dateFormat) {
    case 'MM/dd/yyyy': return `${month}/${day} ${time}`;
    case 'yyyy-MM-dd': return `${month}-${day} ${time}`;
    default:           return `${day}/${month} ${time}`;
  }
}
