import { describe, expect, it } from "vitest";
import { PLAN_DEFINITIONS } from "@app/shared-types";
import {
  canUseAiScan,
  computeEntitlements,
  resolvePlanFromProductId,
  signEntitlementToken,
  trialEndsAt,
  verifyEntitlementToken,
} from "../src/index.js";

const NOW = new Date("2026-08-02T12:00:00Z");
const SECRET = "test-secret-with-length";

describe("entitlements (spec §45–47)", () => {
  it("aktiv prenumeration ger planens förmåner", () => {
    const ent = computeEntitlements(
      { plan: "family", status: "active", expiresAt: new Date("2026-09-01") },
      null,
      { aiScansUsedThisMonth: 10 },
      NOW,
    );
    expect(ent.plan).toBe("family");
    expect(ent.maxHouseholdMembers).toBe(6);
    expect(ent.weekPlanning).toBe(true);
  });
  it("grace period behåller åtkomst (spec §44)", () => {
    const ent = computeEntitlements(
      {
        plan: "household",
        status: "in_grace",
        expiresAt: new Date("2026-08-01"),
        gracePeriodExpiresAt: new Date("2026-08-10"),
      },
      null,
      { aiScansUsedThisMonth: 0 },
      NOW,
    );
    expect(ent.status).toBe("in_grace");
    expect(ent.weekPlanning).toBe(true);
  });
  it("utgången prenumeration utan grace → free", () => {
    const ent = computeEntitlements(
      { plan: "family", status: "active", expiresAt: new Date("2026-07-01") },
      null,
      { aiScansUsedThisMonth: 0 },
      NOW,
    );
    expect(ent.plan).toBe("free");
  });
  it("trial ger full tillgång i 7 dagar (spec §46)", () => {
    const start = new Date("2026-08-01T00:00:00Z");
    const ent = computeEntitlements(
      null,
      { startedAt: start, endsAt: trialEndsAt(start) },
      { aiScansUsedThisMonth: 3 },
      NOW,
    );
    expect(ent.status).toBe("trial");
    expect(ent.weekPlanning).toBe(true);
  });
  it("free tier: 10 skanningar och användbar grund (spec §46)", () => {
    const ent = computeEntitlements(null, null, { aiScansUsedThisMonth: 9 }, NOW);
    expect(ent.plan).toBe("free");
    expect(ent.aiScansPerMonth).toBe(10);
    expect(canUseAiScan(ent)).toBe(true);
    expect(canUseAiScan({ ...ent, aiScansUsedThisMonth: 10 })).toBe(false);
  });
  it("mappar productId → plan (härlett ur brand-konfig, aldrig hårdkodat)", () => {
    expect(resolvePlanFromProductId(PLAN_DEFINITIONS.family.appleProductId)).toBe("family");
    expect(resolvePlanFromProductId(PLAN_DEFINITIONS.household.googleProductId)).toBe("household");
    expect(resolvePlanFromProductId("okänd")).toBeNull();
  });
});

describe("signerad entitlement-token (spec §44)", () => {
  const ent = computeEntitlements(
    { plan: "family", status: "active", expiresAt: new Date("2026-09-01") },
    null,
    { aiScansUsedThisMonth: 0 },
    NOW,
  );

  it("signerar och verifierar", () => {
    const token = signEntitlementToken("user-1", ent, SECRET, { now: NOW });
    const result = verifyEntitlementToken(token, SECRET, NOW);
    expect(result.valid).toBe(true);
    if (result.valid) {
      expect(result.payload.sub).toBe("user-1");
      expect(result.payload.plan).toBe("family");
      expect(result.withinGrace).toBe(false);
    }
  });
  it("avvisar manipulerad token", () => {
    const token = signEntitlementToken("user-1", ent, SECRET, { now: NOW });
    const tampered = token.slice(0, -4) + "AAAA";
    expect(verifyEntitlementToken(tampered, SECRET, NOW).valid).toBe(false);
  });
  it("avvisar fel secret", () => {
    const token = signEntitlementToken("user-1", ent, SECRET, { now: NOW });
    expect(verifyEntitlementToken(token, "annan-secret-123", NOW).valid).toBe(false);
  });
  it("grace: giltig efter exp men inom graceExp, ogiltig därefter – ALDRIG permanent boolean", () => {
    const token = signEntitlementToken("user-1", ent, SECRET, {
      now: NOW,
      ttlHours: 24,
      graceDays: 7,
    });
    const afterExp = new Date(NOW.getTime() + 3 * 86_400_000);
    const inGrace = verifyEntitlementToken(token, SECRET, afterExp);
    expect(inGrace.valid).toBe(true);
    if (inGrace.valid) expect(inGrace.withinGrace).toBe(true);

    const afterGrace = new Date(NOW.getTime() + 9 * 86_400_000);
    const expired = verifyEntitlementToken(token, SECRET, afterGrace);
    expect(expired.valid).toBe(false);
    if (!expired.valid) expect(expired.reason).toBe("expired_beyond_grace");
  });
});
