import { describe, expect, it } from "vitest";
import { PortfolioStatus } from "@/app/db/schema/portfolio";
import {
  formatMoney,
  formatUpdatedAgo,
  matchesSearch,
  sortPortfolios,
  statusDisplay,
  upsertLocalConfig,
} from "./portfolioHomeView";

describe("statusDisplay", () => {
  it("maps lifecycle enum values to clean pill labels", () => {
    expect(statusDisplay("scoping")).toEqual({
      label: "Scoping",
      tone: "early",
      needsAttention: false,
    });
    expect(statusDisplay("completion; status: at risk")).toEqual({
      label: "At risk",
      tone: "atrisk",
      needsAttention: true,
    });
    expect(statusDisplay("needs review")).toEqual({
      label: "Needs review",
      tone: "review",
      needsAttention: true,
    });
  });

  it("covers every status the schema allows", () => {
    for (const status of PortfolioStatus) {
      const display = statusDisplay(status);
      expect(display.label, status).toBeTruthy();
      expect(display.label).not.toContain(";");
    }
  });
});

describe("formatMoney", () => {
  it("renders millions with one decimal and thousands as whole k", () => {
    expect(formatMoney(6500000)).toBe("£6.5M");
    expect(formatMoney(4000000)).toBe("£4M");
    expect(formatMoney(745000)).toBe("£745k");
    expect(formatMoney(0)).toBe("£0");
  });
});

describe("matchesSearch", () => {
  const lambeth = {
    name: "Lambeth Regeneration",
    status: "completion; status: at risk",
    goal: "Increasing EPC",
    folderName: "SHDF Wave 3",
  };

  it("matches on name, display status, goal, and folder name, case-insensitively", () => {
    expect(matchesSearch(lambeth, "lambeth")).toBe(true);
    expect(matchesSearch(lambeth, "at risk")).toBe(true);
    expect(matchesSearch(lambeth, "epc")).toBe(true);
    expect(matchesSearch(lambeth, "shdf")).toBe(true);
    expect(matchesSearch(lambeth, "peabody")).toBe(false);
  });

  it("matches everything on an empty query and unfoldered portfolios don't match folder terms", () => {
    expect(matchesSearch(lambeth, "  ")).toBe(true);
    expect(
      matchesSearch({ ...lambeth, folderName: null }, "shdf"),
    ).toBe(false);
  });
});

describe("sortPortfolios", () => {
  const rows = [
    { name: "B", status: "scoping", numberOfProperties: 10, updatedAt: new Date("2026-07-01") },
    { name: "A", status: "completion; status: delayed", numberOfProperties: 30, updatedAt: new Date("2026-07-06") },
    { name: "C", status: "completion; status: at risk", numberOfProperties: 20, updatedAt: new Date("2026-07-03") },
  ];
  const names = (sorted: typeof rows) => sorted.map((r) => r.name);

  it("orders by the chosen key without mutating the input", () => {
    expect(names(sortPortfolios(rows, "updated"))).toEqual(["A", "C", "B"]);
    expect(names(sortPortfolios(rows, "attention"))).toEqual(["C", "A", "B"]);
    expect(names(sortPortfolios(rows, "name"))).toEqual(["A", "B", "C"]);
    expect(names(sortPortfolios(rows, "props"))).toEqual(["A", "C", "B"]);
    expect(names(rows)).toEqual(["B", "A", "C"]);
  });
});

describe("upsertLocalConfig", () => {
  it("creates a config on first star, like the lazy DB upsert", () => {
    const next = upsertLocalConfig([], "7", { starredAt: "2026-07-07T12:00:00Z" });

    expect(next).toEqual([
      { portfolioId: "7", folderId: null, starredAt: "2026-07-07T12:00:00Z" },
    ]);
  });

  it("changing one field preserves the other", () => {
    const configs = [
      { portfolioId: "7", folderId: "10", starredAt: "2026-07-01T00:00:00Z" },
    ];

    expect(upsertLocalConfig(configs, "7", { starredAt: null })).toEqual([
      { portfolioId: "7", folderId: "10", starredAt: null },
    ]);
    expect(upsertLocalConfig(configs, "7", { folderId: null })).toEqual([
      { portfolioId: "7", folderId: null, starredAt: "2026-07-01T00:00:00Z" },
    ]);
  });
});

describe("formatUpdatedAgo", () => {
  const now = new Date("2026-07-07T12:00:00Z");

  it("reads naturally at every distance", () => {
    expect(formatUpdatedAgo("2026-07-07T09:00:00Z", now)).toBe("today");
    expect(formatUpdatedAgo("2026-07-06T09:00:00Z", now)).toBe("yesterday");
    expect(formatUpdatedAgo("2026-07-02T12:00:00Z", now)).toBe("5 days ago");
    expect(formatUpdatedAgo("2026-05-01T00:00:00Z", now)).toBe("2 months ago");
  });
});
