import gleam/io
import gleam/list
import glam/doc.{type Document}

pub type TodoList =
  List(Task)

pub type Task {
  Task(status: Status, description: String, subtasks: TodoList)
}

pub type Status {
  Todo
  Done
  InProgress
}

/// Pretty prints a `TodoList`:
/// - each task is preceded by a bullet that indicates its status
/// - each list of subtasks is indented by 2 spaces more than its father
/// - each task should _always_ be on its own line, no matter how wide the
///   maximum line width
///
/// ## Examples
///
/// ```gleam
/// [
///   Task(Todo, "foo", []),
///   Task(InProgress, "bar", []),
///   Task(Done, "baz", []),
/// ]
/// |> tasks_to_doc
/// |> doc.to_string(10_000)
/// // ->
/// // - [ ] foo
/// // - […] bar
/// // - [X] baz
/// ```
///
/// ```gleam
/// [
///   Task(InProgress, "foo", [
///     Task(Done, "bar", [
///       Task(Todo, "baz", [])
///     ])
///   ]),
/// ]
/// |> tasks_to_doc
/// |> doc.to_string(10_000)
/// // ->
/// // - […] foo
/// //   - [X] bar
/// //     - [ ] baz
/// ```
///
pub fn tasks_to_doc(tasks: TodoList) -> Document {
  list.map(tasks, task_to_doc)
  |> doc.join(with: doc.soft_break)
  |> doc.force_break
}

fn task_to_doc(task: Task) -> Document {
  let task_line = status_to_bullet(task.status) <> " " <> task.description
  let task_doc = doc.from_string(task_line)

  case list.is_empty(task.subtasks) {
    True -> task_doc
    False ->
      [task_doc, doc.soft_break, tasks_to_doc(task.subtasks)]
      |> doc.concat
      |> doc.nest(by: 2)
  }
}

fn status_to_bullet(status: Status) -> String {
  case status {
    Todo -> "- [ ]"
    Done -> "- [X]"
    InProgress -> "- […]"
  }
}

pub fn example_todo_list() -> TodoList {
  [
    Task(InProgress, "publish Glam v1.1.0", [
      Task(InProgress, "write a tutorial on todo lists", []),
      Task(InProgress, "add `doc.flex_break`", [
        Task(Done, "add the appropriate type variant", []),
        Task(Done, "implement the missing cases", []),
        Task(Todo, "add some tests", []),
      ]),
    ]),
    Task(Todo, "get some sleep", []),
  ]
}

pub fn main() {
  example_todo_list()
  |> tasks_to_doc
  |> doc.to_string(10_000)
  |> io.println
}
