import { getLang } from "../i18n";
import type { ViewRow } from "./types";

// Shared date/time formatters for the views shapes (list / cards / calendar).
//
// The substrate marshals deadline.due_date as time.Date(...,0,0,0,0,UTC), so
// the JSON arrives as "YYYY-MM-DDT00:00:00Z" — UTC midnight, no real time
// component. Feeding that into new Date() + toLocaleTimeString() in a
// non-UTC browser produces "02:00" (CEST), "01:00" (CET), "20:00" the day
// before (EST), and so on — a phantom hour that the source data never had.
//
// The fix is to recognise the date-only shape and either render the date
// (formatted in UTC so the day matches the source day everywhere) or render
// nothing in the time slot. The kind-aware helpers below thread that
// distinction through the shapes; see t-paliad-153.

const DATE_ONLY_RE = /^(\d{4})-(\d{2})-(\d{2})(?:T00:00:00(?:\.0+)?Z)?$/;

export function isDateOnly(iso: string): boolean {
  return typeof iso === "string" && DATE_ONLY_RE.test(iso);
}

export function parseDateOnly(iso: string): Date | null {
  if (typeof iso !== "string") return null;
  const m = iso.match(DATE_ONLY_RE);
  if (!m) return null;
  const d = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3])));
  return isNaN(d.getTime()) ? null : d;
}

function locale(): string {
  return getLang() === "de" ? "de-DE" : "en-GB";
}

export function formatDate(iso: string): string {
  const dateOnly = parseDateOnly(iso);
  if (dateOnly) {
    return dateOnly.toLocaleDateString(locale(), {
      day: "2-digit", month: "2-digit", year: "numeric", timeZone: "UTC",
    });
  }
  const d = new Date(iso);
  if (isNaN(d.getTime())) return iso;
  return d.toLocaleDateString(locale(), {
    day: "2-digit", month: "2-digit", year: "numeric",
  });
}

export function formatLongDate(iso: string): string {
  const dateOnly = parseDateOnly(iso);
  if (dateOnly) {
    return dateOnly.toLocaleDateString(locale(), {
      weekday: "long", year: "numeric", month: "long", day: "numeric", timeZone: "UTC",
    });
  }
  const d = new Date(iso);
  if (isNaN(d.getTime())) return iso;
  return d.toLocaleDateString(locale(), {
    weekday: "long", year: "numeric", month: "long", day: "numeric",
  });
}

// formatTime returns "" for date-only inputs — they have no real time and
// rendering them as HH:MM leaks the local UTC offset.
export function formatTime(iso: string): string {
  if (isDateOnly(iso)) return "";
  const d = new Date(iso);
  if (isNaN(d.getTime())) return iso;
  return d.toLocaleTimeString(locale(), { hour: "2-digit", minute: "2-digit" });
}

// formatRowTime: the time-slot helper used by shape-cards. When the
// surrounding shape already shows the date (e.g. day-grouped headings),
// deadlines render nothing — the date is implicit. Otherwise the deadline
// row falls back to its date so the user still knows when it's due.
export function formatRowTime(row: ViewRow, opts: { dateAvailable: boolean }): string {
  if (row.kind === "deadline" || isDateOnly(row.event_date)) {
    return opts.dateAvailable ? "" : formatDate(row.event_date);
  }
  return formatTime(row.event_date);
}

// formatRelative: deadlines reduce to day precision so a Frist due
// "tomorrow" never shows up as "in 2h" because of the UTC offset.
export function formatRelative(iso: string, kind?: ViewRow["kind"]): string {
  if (kind === "deadline" || isDateOnly(iso)) return formatDayRelative(iso);
  return formatMomentRelative(iso);
}

function formatDayRelative(iso: string): string {
  const due = parseDateOnly(iso);
  if (!due) return formatMomentRelative(iso);
  const today = new Date();
  const todayUTC = Date.UTC(today.getFullYear(), today.getMonth(), today.getDate());
  const diffDays = Math.round((due.getTime() - todayUTC) / 86400000);
  const lang = getLang();
  if (diffDays < 0) {
    const n = Math.abs(diffDays);
    return lang === "de"
      ? (n === 1 ? "vor 1 Tag" : `vor ${n} Tagen`)
      : (n === 1 ? "1 day ago" : `${n} days ago`);
  }
  if (diffDays === 0) return lang === "de" ? "heute" : "today";
  if (diffDays === 1) return lang === "de" ? "morgen" : "tomorrow";
  return lang === "de" ? `in ${diffDays} Tagen` : `in ${diffDays} days`;
}

function formatMomentRelative(iso: string): string {
  const t0 = Date.parse(iso);
  if (isNaN(t0)) return iso;
  const diffMs = t0 - Date.now();
  const past = diffMs < 0;
  const sec = Math.abs(Math.floor(diffMs / 1000));
  const lang = getLang();
  if (sec < 60) return past ? (lang === "de" ? `vor ${sec}s` : `${sec}s ago`) : (lang === "de" ? `in ${sec}s` : `in ${sec}s`);
  const min = Math.floor(sec / 60);
  if (min < 60) return past ? (lang === "de" ? `vor ${min}m` : `${min}m ago`) : (lang === "de" ? `in ${min}m` : `in ${min}m`);
  const hr = Math.floor(min / 60);
  if (hr < 24) return past ? (lang === "de" ? `vor ${hr}h` : `${hr}h ago`) : (lang === "de" ? `in ${hr}h` : `in ${hr}h`);
  const day = Math.floor(hr / 24);
  return past ? (lang === "de" ? `vor ${day}d` : `${day}d ago`) : (lang === "de" ? `in ${day}d` : `in ${day}d`);
}
