import { DateTime } from "luxon"
import { Todo } from "./todo"
import { test, expect } from "bun:test"

test("parsing valid todos", () => {
  expectTodo("[ ] some words and that is it", { done: false, text: "some words and that is it" })
  expectTodo("[x] has #some #tags and @2/5/25 5m", {
    done: true,
    text: "has #some #tags and @2/5/25 5m",
    tags: ["some", "tags"],
    timeEstimate: 300,
    dueDate: DateTime.fromISO("2025-02-05"),
  })
  expectTodo("[ ] works with iso dates @2055/01/2", {
    text: "works with iso dates @2055/01/2",
    dueDate: DateTime.fromISO("2055-01-02"),
  })
  expectTodo("  [ ] handles nested todos", { text: "handles nested todos", indent: "  " })
})

test("todo nodes", () => {
  const todo = Todo.parse("  [ ] some #words and @2/2/25")
  expect(todo!.nodes).toEqual([
    { type: "indent", content: "  " },
    { type: "checkbox", content: "[ ] ", checked: false },
    { type: "text", content: "some" },
    { type: "whitespace", content: " " },
    { type: "tag", content: "#words" },
    { type: "whitespace", content: " " },
    { type: "text", content: "and" },
    { type: "whitespace", content: " " },
    { type: "date", content: "@2/2/25", parsed: DateTime.fromISO("2025-02-02") },
  ])
})

test("parsing invalid todos", () => {
  expectInvalidTodo("[ ")
  expectInvalidTodo(" -[]")
  expectInvalidTodo("-[] some text")
})

// Helpers
test("todo to string", () => {
  const todo = Todo.parse("[ ] some #words and @2/2/25")
  expect(todo!.toString()).toBe("[ ] some #words and @2/2/25")

  const nestedTodo = Todo.parse("  [x] more #words and @2/2/25")
  expect(nestedTodo!.toString()).toBe("  [x] more #words and @2/2/25")
})

const expectTodo = (input: string, expectation: Partial<Todo>) => {
  const todo = Todo.parse(input)
  if (!todo) {
    throw new Error(`Failed to parse todo line: "${input}"`)
  }

  for (const key in expectation) {
    const typedKey = key as keyof Todo
    const expectedValue = expectation[typedKey]
    expect(todo[typedKey], `Unexpected "${typedKey}" for todo "${input}"`).toEqual(expectedValue!)
  }
}

const expectInvalidTodo = (input: string) => {
  test(`parse invalid todo: ${input}`, () => {
    const todo = Todo.parse(input)
    expect(todo, `Expected parsing to fail for: "${input}"`).toBeUndefined()
  })
}
