/*!
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
import dayjs from "dayjs";
import dayjsDuration from "dayjs/plugin/duration";
import { describe, it, expect, vi, beforeAll, afterAll } from "vitest";

import { getDuration, renderDuration, getRelativeTime } from "./datetimeUtils";

dayjs.extend(dayjsDuration);

describe("getDuration & formatDuration", () => {
  it("handles durations less than 60 seconds", () => {
    const start = "2024-03-14T10:00:00.000Z";
    const end = "2024-03-14T10:00:05.5111111Z";

    expect(getDuration(start, end)).toBe("00:00:05.511");
  });

  it("handles durations spanning multiple days", () => {
    const start = "2024-03-14T10:00:00.000Z";
    const end = "2024-03-17T15:30:45.000Z";

    expect(getDuration(start, end)).toBe("3d05:30:45");
  });

  it("handles exactly 24 hours", () => {
    const start = "2024-03-14T10:00:00.000Z";
    const end = "2024-03-15T10:00:00.000Z";

    expect(getDuration(start, end)).toBe("1d00:00:00");
  });

  it("handles hours and minutes without days", () => {
    const start = "2024-03-14T10:00:00.000Z";
    const end = "2024-03-14T12:30:00.000Z";

    expect(getDuration(start, end)).toBe("02:30:00");
  });

  it("omits milliseconds when withMilliseconds is false", () => {
    const start = "2024-03-14T10:00:00.000Z";
    const end = "2024-03-14T10:00:05.511Z";

    expect(getDuration(start, end, false)).toBe("00:00:05");
  });

  it("handles small, null or undefined values", () => {
    expect(getDuration(null, null)).toBe(undefined);
    expect(getDuration(undefined, undefined)).toBe(undefined);
    expect(getDuration(null, "2024-03-14T10:00:10.000Z")).toBe(undefined);
    expect(renderDuration(0.000_01)).toBe(undefined);
  });

  it("falls back to current time when endDate is null (running task)", () => {
    vi.useFakeTimers();
    vi.setSystemTime(new Date("2024-03-14T10:00:10.000Z"));

    const start = "2024-03-14T10:00:00.000Z";

    expect(getDuration(start, null)).toBe("00:00:10");
    expect(getDuration(start, undefined)).toBe("00:00:10");

    vi.useRealTimers();
  });

  it("handles both numbers and duration objects", () => {
    expect(renderDuration(dayjs.duration(10, "seconds"))).toBe("00:00:10");
    expect(renderDuration(10)).toBe("00:00:10");
  });

  it("handles floating point milliseconds", () => {
    expect(renderDuration(dayjs.duration(10.000_499_738, "seconds"))).toBe("00:00:10");
    expect(renderDuration(10.000_499_738)).toBe("00:00:10");
    expect(renderDuration(dayjs.duration(10.000_500, "seconds"))).toBe("00:00:10.001");
    expect(renderDuration(10.000_500)).toBe("00:00:10.001");
    expect(renderDuration(dayjs.duration(10.838_999_738, "seconds"))).toBe("00:00:10.839");
    expect(renderDuration(10.838_999_738)).toBe("00:00:10.839");
  });
});

describe("getRelativeTime", () => {
  const fixedNow = new Date("2024-03-14T10:00:10.000Z");

  beforeAll(() => {
    vi.useFakeTimers();
    vi.setSystemTime(fixedNow);
  });

  afterAll(() => {
    vi.useRealTimers();
  });

  it("returns relative time for a valid date", () => {
    const date = "2024-03-14T10:00:00.000Z";

    expect(getRelativeTime(date)).toBe("a few seconds ago");
  });

  it("returns an empty string for undefined dates", () => {
    expect(getRelativeTime(undefined)).toBe("");
  });

  it("handles future dates", () => {
    const futureDate = "2024-03-14T10:00:20.000Z";

    expect(getRelativeTime(futureDate)).toBe("in a few seconds");
  });
});
