import { describe, it, expect } from "vitest";
import { resolveReportsPeriod, parseAccountIds, serializeAccountIds } from "./useReportsPeriod";

describe("resolveReportsPeriod", () => {
  const fixedToday = new Date("2026-04-14T12:00:00Z");

  it("defaults to current civil year when no URL params are set", () => {
    const result = resolveReportsPeriod(null, null, null, fixedToday);
    expect(result.from).toBe("2026-01-01");
    expect(result.to).toBe("2026-12-31");
    expect(result.period).toBe("custom");
  });

  it("restores state from bookmarked from/to params", () => {
    const result = resolveReportsPeriod("2025-03-01", "2025-06-30", null, fixedToday);
    expect(result.from).toBe("2025-03-01");
    expect(result.to).toBe("2025-06-30");
    expect(result.period).toBe("custom");
  });

  it("keeps period=yearly alongside explicit from/to", () => {
    const result = resolveReportsPeriod("2024-01-01", "2024-12-31", "year", fixedToday);
    expect(result.period).toBe("year");
  });

  it("ignores malformed dates and falls back to the civil year", () => {
    const result = resolveReportsPeriod("not-a-date", "also-not", null, fixedToday);
    expect(result.from).toBe("2026-01-01");
    expect(result.to).toBe("2026-12-31");
    expect(result.period).toBe("custom");
  });

  it("resolves preset period values without from/to", () => {
    const result = resolveReportsPeriod(null, null, "6months", fixedToday);
    expect(result.period).toBe("6months");
    expect(result.from).toBeTruthy();
    expect(result.to).toBeTruthy();
  });

  it("rejects an invalid period string and falls back to civil year custom", () => {
    const result = resolveReportsPeriod(null, null, "bogus", fixedToday);
    expect(result.period).toBe("custom");
    expect(result.from).toBe("2026-01-01");
  });

  it("treats `all` as a preset with empty range (service handles the clauses)", () => {
    const result = resolveReportsPeriod(null, null, "all", fixedToday);
    expect(result.period).toBe("all");
    // Fallback civil year when computeDateRange returns empty
    expect(result.from).toBe("2026-01-01");
    expect(result.to).toBe("2026-12-31");
  });
});

describe("parseAccountIds", () => {
  it("defaults to an empty array (no filter) when the sources param is absent", () => {
    expect(parseAccountIds(null)).toEqual([]);
  });

  it("defaults to an empty array for an empty string", () => {
    expect(parseAccountIds("")).toEqual([]);
  });

  it("parses a comma-separated list of ids", () => {
    expect(parseAccountIds("3,7,12")).toEqual([3, 7, 12]);
  });

  it("trims whitespace around tokens", () => {
    expect(parseAccountIds(" 3 , 7 ,12")).toEqual([3, 7, 12]);
  });

  it("parses a single id", () => {
    expect(parseAccountIds("42")).toEqual([42]);
  });

  it("drops non-numeric tokens but keeps the valid ones (rejects non-numeric)", () => {
    expect(parseAccountIds("3,abc,7")).toEqual([3, 7]);
  });

  it("drops decimal tokens (not a finite integer)", () => {
    expect(parseAccountIds("3,4.5,7")).toEqual([3, 7]);
  });

  it("drops empty tokens produced by stray/trailing commas", () => {
    expect(parseAccountIds("3,,7,")).toEqual([3, 7]);
  });

  it("drops exponential/hex-looking tokens that Number() would otherwise coerce", () => {
    expect(parseAccountIds("3,1e3,0x10,7")).toEqual([3, 7]);
  });

  it("drops unsafe-integer tokens (too large to be a reliable id)", () => {
    expect(parseAccountIds("3,99999999999999999999,7")).toEqual([3, 7]);
  });

  it("returns an empty array when every token is invalid", () => {
    expect(parseAccountIds("abc,def")).toEqual([]);
  });
});

describe("serializeAccountIds", () => {
  it("joins ids into a comma-separated string", () => {
    expect(serializeAccountIds([3, 7, 12])).toBe("3,7,12");
  });

  it("serializes a single id", () => {
    expect(serializeAccountIds([42])).toBe("42");
  });

  it("returns null for an empty array so the param is removed (no filter)", () => {
    expect(serializeAccountIds([])).toBeNull();
  });

  it("drops invalid entries (e.g. NaN) before serializing", () => {
    expect(serializeAccountIds([3, NaN, 7])).toBe("3,7");
  });

  it("returns null when every entry is invalid", () => {
    expect(serializeAccountIds([NaN, Infinity])).toBeNull();
  });

  it("round-trips through parseAccountIds", () => {
    const ids = [1, 2, 3];
    expect(parseAccountIds(serializeAccountIds(ids))).toEqual(ids);
  });
});
