import { describe, expect, it } from "vitest";
import { detectTaskIntent, parseTaskDueAt } from "./task-detect";

/** Corpus of realistic room chatter. The point of the table is that the
 * thresholds in task-detect.ts can't be nudged without a failing case
 * showing which side of the line moved. */
const SHOULD_FIRE = [
  "TODO: rotate the staging credentials",
  "todo update the changelog",
  "Action item: split the billing service out",
  "[ ] write the migration notes",
  "Can you implement the user flow in Tasks",
  "Could someone review my PR before EOD?",
  "please update the runbook",
  "@ana can you check the failing test",
  "Fix the login bug",
  "Deploy the hotfix to staging",
  "we need to migrate the remaining rows",
  "let's schedule the retro for Friday",
  "don't forget to bump the version",
  "make sure the invite emails go out today",
  "Draft the announcement by tomorrow morning",
  "do this by tomorrow",
  "I want this done by tomorrow",
  "can you do this by sunday",
  "should we revert the release?",
];

const SHOULD_NOT_FIRE = [
  "hi",
  "hey!",
  "thanks 👍",
  "lgtm",
  "ok",
  "I fixed the login bug",
  "just shipped the hotfix",
  "the migration has been done already",
  "FYI the deploy finished",
  "nice work on the release",
  "check out this article",
  "take a look when you get a sec",
  "look at how fast that got merged",
  "wondering if anyone else has seen this",
  "the deploy is broken",
  "standup in 5",
  "?",
  "",
];

describe("detectTaskIntent", () => {
  it.each(SHOULD_FIRE)("flags %j as a task", (text) => {
    const result = detectTaskIntent(text);
    expect(result.isTask, `expected a task, got score ${result.score}`).toBe(true);
    expect(result.reason).not.toBe("");
  });

  it.each(SHOULD_NOT_FIRE)("leaves %j alone", (text) => {
    expect(detectTaskIntent(text).isTask, `unexpected match: ${detectTaskIntent(text).reason}`).toBe(false);
  });

  it("handles null and undefined", () => {
    expect(detectTaskIntent(null).isTask).toBe(false);
    expect(detectTaskIntent(undefined).isTask).toBe(false);
  });

  it("lets an explicit marker beat the past-tense veto", () => {
    // "I fixed" alone is a report; "TODO" makes it a note-to-self task.
    expect(detectTaskIntent("I fixed the symptom — TODO: find the root cause").isTask).toBe(true);
  });

  it("does not count a bare mention as a signal", () => {
    expect(detectTaskIntent("@ana ^").isTask).toBe(false);
  });
});

describe("parseTaskDueAt", () => {
  // Fixed Wednesday so "tomorrow" / weekday math is deterministic.
  const wed = new Date(2026, 7, 26, 15, 0, 0); // Wed Aug 26 2026 local

  it("parses tomorrow as the next calendar day at local midnight", () => {
    expect(parseTaskDueAt("do this by tomorrow", wed)).toBe(new Date(2026, 7, 27).toISOString());
  });

  it("parses next Friday / next Sunday", () => {
    expect(parseTaskDueAt("finish this next Friday", wed)).toBe(new Date(2026, 7, 28).toISOString());
    expect(parseTaskDueAt("do this by next Sunday", wed)).toBe(new Date(2026, 7, 30).toISOString());
  });

  it("parses by Sunday as the upcoming Sunday", () => {
    expect(parseTaskDueAt("can you do this by sunday", wed)).toBe(new Date(2026, 7, 30).toISOString());
  });

  it("returns null when no date cue is present", () => {
    expect(parseTaskDueAt("please review the PR", wed)).toBeNull();
  });
});
