import { describe, expect, test } from "bun:test";
import {
  formatDate,
  formatRelative,
  formatRowTime,
  formatTime,
  isDateOnly,
  parseDateOnly,
} from "./format";
import type { ViewRow } from "./types";

// Regression tests for t-paliad-153: deadline due_date renders as 02:00 in
// CEST. The substrate marshals deadline.due_date as "YYYY-MM-DDT00:00:00Z";
// the formatters must treat that as a calendar day with no time component.

const stubRow = (overrides: Partial<ViewRow> = {}): ViewRow => ({
  kind: "deadline",
  id: "00000000-0000-0000-0000-000000000000",
  title: "Call me",
  event_date: "2026-05-08T00:00:00Z",
  detail: {},
  ...overrides,
});

describe("isDateOnly / parseDateOnly", () => {
  test("recognises YYYY-MM-DD", () => {
    expect(isDateOnly("2026-05-08")).toBe(true);
    expect(parseDateOnly("2026-05-08")).not.toBeNull();
  });

  test("recognises the substrate's UTC-midnight serialisation", () => {
    expect(isDateOnly("2026-05-08T00:00:00Z")).toBe(true);
    expect(parseDateOnly("2026-05-08T00:00:00Z")).not.toBeNull();
  });

  test("rejects timestamps with a real time component", () => {
    expect(isDateOnly("2026-05-08T14:30:00Z")).toBe(false);
    expect(parseDateOnly("2026-05-08T14:30:00Z")).toBeNull();
  });

  test("rejects garbage", () => {
    expect(isDateOnly("not-a-date")).toBe(false);
    expect(parseDateOnly("not-a-date")).toBeNull();
  });
});

describe("formatTime", () => {
  test("returns empty string for date-only inputs (no phantom 02:00)", () => {
    expect(formatTime("2026-05-08T00:00:00Z")).toBe("");
    expect(formatTime("2026-05-08")).toBe("");
  });

  test("renders HH:MM for real timestamps", () => {
    expect(formatTime("2026-05-08T14:30:00Z")).toMatch(/\d{2}:\d{2}/);
  });
});

describe("formatDate", () => {
  test("date-only input formats the source day in any timezone", () => {
    // Whatever locale getLang() resolves to, the day portion must be 08.
    const out = formatDate("2026-05-08T00:00:00Z");
    expect(out).toContain("08");
    expect(out).toContain("2026");
  });
});

describe("formatRowTime", () => {
  test("deadline + dateAvailable=true returns empty (heading shows the day)", () => {
    expect(formatRowTime(stubRow(), { dateAvailable: true })).toBe("");
  });

  test("deadline + dateAvailable=false falls back to the date", () => {
    expect(formatRowTime(stubRow(), { dateAvailable: false })).toContain("2026");
  });

  test("appointment with a real start_at still renders HH:MM", () => {
    const row = stubRow({ kind: "appointment", event_date: "2026-05-08T14:30:00Z" });
    expect(formatRowTime(row, { dateAvailable: true })).toMatch(/\d{2}:\d{2}/);
  });

  test("appointment with date-only event_date does not leak phantom time", () => {
    const row = stubRow({ kind: "appointment", event_date: "2026-05-08T00:00:00Z" });
    // Belt-and-braces: even if a stray date-only value shows up under a
    // non-deadline kind, the helper detects it and returns "" instead of
    // "02:00" / "01:00" / etc.
    expect(formatRowTime(row, { dateAvailable: true })).toBe("");
  });
});

describe("formatRelative", () => {
  test("deadline kind reduces to day precision", () => {
    const today = new Date();
    const todayISO = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}T00:00:00Z`;
    const out = formatRelative(todayISO, "deadline");
    expect(out.toLowerCase()).toMatch(/heute|today/);
  });

  test("date-only iso reduces to day precision even without an explicit kind", () => {
    const today = new Date();
    const todayISO = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
    const out = formatRelative(todayISO);
    expect(out.toLowerCase()).toMatch(/heute|today/);
  });

  test("real timestamp keeps moment-precision relative", () => {
    const inAnHour = new Date(Date.now() + 60 * 60 * 1000).toISOString();
    expect(formatRelative(inAnHour, "appointment")).toMatch(/\d/);
  });
});
