import { describe, expect, it } from "vitest";
import { applyDecay, computeTrust, householdTrustScore, worstTrustState } from "../src/trust.js";

const PINNED = new Date("2026-08-06T12:00:00.000Z");

describe("applyDecay", () => {
  it("returns original confidence when elapsed days is zero", () => {
    expect(applyDecay(0.8, 0)).toBe(0.8);
  });

  it("halves confidence after halfLifeDays", () => {
    expect(applyDecay(1.0, 7, { halfLifeDays: 7, staleAfterDays: 30 })).toBeCloseTo(0.5, 3);
  });

  it("returns zero when elapsed days reaches staleAfterDays", () => {
    expect(applyDecay(0.8, 30, { halfLifeDays: 7, staleAfterDays: 30 })).toBe(0);
  });
});

describe("computeTrust", () => {
  it("returns trusted for a fresh high-confidence item", () => {
    const result = computeTrust(
      {
        confidence: 0.8,
        verifiedByUser: false,
        lastVerifiedAt: null,
        quantity: 2,
        updatedAt: PINNED,
      },
      PINNED,
    );
    expect(result.state).toBe("trusted");
    expect(result.score).toBeGreaterThanOrEqual(60);
  });

  it("decays when last verification is old", () => {
    const sevenDaysAgo = new Date(PINNED.getTime() - 7 * 86_400_000);
    const result = computeTrust(
      {
        confidence: 0.8,
        verifiedByUser: true,
        lastVerifiedAt: sevenDaysAgo,
        quantity: 1,
        updatedAt: sevenDaysAgo,
      },
      PINNED,
    );
    expect(result.state).toBe("decaying");
  });

  it("is stale for very old unverified low-confidence items", () => {
    const sixtyDaysAgo = new Date(PINNED.getTime() - 60 * 86_400_000);
    const result = computeTrust(
      {
        confidence: 0.2,
        verifiedByUser: false,
        lastVerifiedAt: null,
        quantity: 1,
        updatedAt: sixtyDaysAgo,
      },
      PINNED,
    );
    expect(result.state).toBe("stale");
  });

  it("is trusted when user verified recently", () => {
    const result = computeTrust(
      {
        confidence: 0.7,
        verifiedByUser: true,
        lastVerifiedAt: PINNED,
        quantity: 1,
        updatedAt: PINNED,
      },
      PINNED,
    );
    expect(result.state).toBe("trusted");
    expect(result.score).toBeGreaterThanOrEqual(70);
  });

  it("uses custom decay profile to accelerate staleness", () => {
    const tenDaysAgo = new Date(PINNED.getTime() - 10 * 86_400_000);
    const fast = { halfLifeDays: 3, staleAfterDays: 10 };
    const result = computeTrust(
      {
        confidence: 0.9,
        verifiedByUser: true,
        lastVerifiedAt: tenDaysAgo,
        quantity: 1,
        updatedAt: tenDaysAgo,
      },
      PINNED,
      fast,
    );
    expect(result.state).toBe("stale");
  });
});

describe("householdTrustScore", () => {
  it("returns 100 / up_to_date for empty inventory", () => {
    const result = householdTrustScore({ items: [], correctionCount30d: 0, transactionCount30d: 0 }, PINNED);
    expect(result.score).toBe(100);
    expect(result.status).toBe("up_to_date");
  });

  it("scores high when all items are verified and recent", () => {
    const result = householdTrustScore(
      {
        items: [
          { confidence: 1, verifiedByUser: true, lastVerifiedAt: PINNED, quantity: 2, updatedAt: PINNED },
          { confidence: 1, verifiedByUser: true, lastVerifiedAt: PINNED, quantity: 3, updatedAt: PINNED },
        ],
        correctionCount30d: 0,
        transactionCount30d: 0,
      },
      PINNED,
    );
    expect(result.score).toBeGreaterThanOrEqual(80);
    expect(result.status).toBe("up_to_date");
  });

  it("drops to uncertain when items are old and unverified", () => {
    const old = new Date(PINNED.getTime() - 60 * 86_400_000);
    const result = householdTrustScore(
      {
        items: [
          { confidence: 0.3, verifiedByUser: false, lastVerifiedAt: null, quantity: 0.1, updatedAt: old },
        ],
        correctionCount30d: 5,
        transactionCount30d: 10,
      },
      PINNED,
    );
    expect(result.status).toBe("uncertain");
    expect(result.score).toBeLessThan(50);
  });

  it("flags needs_check for partly verified household", () => {
    const weekAgo = new Date(PINNED.getTime() - 7 * 86_400_000);
    const result = householdTrustScore(
      {
        items: [
          { confidence: 0.8, verifiedByUser: true, lastVerifiedAt: weekAgo, quantity: 1, updatedAt: weekAgo },
          { confidence: 0.5, verifiedByUser: false, lastVerifiedAt: null, quantity: 1, updatedAt: weekAgo },
        ],
        correctionCount30d: 1,
        transactionCount30d: 5,
      },
      PINNED,
    );
    expect(result.status).toBe("needs_check");
  });
});

describe("worstTrustState", () => {
  it("returns stale if present", () => {
    expect(worstTrustState(["trusted", "stale", "decaying"])).toBe("stale");
  });

  it("falls back to unverified", () => {
    expect(worstTrustState(["trusted", "unverified"])).toBe("unverified");
  });
});
