---
title: "Tournament Lifecycle and Live Operations"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Tournament Lifecycle and Live Operations}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  eval = FALSE
)
```

```{r setup}
library(bracketeer)
```

This guide is a function-discovery map for tournament operations. Every
public function is shown in context so you know what it does, when to call
it, and what you get back.

---

## Mental model

A tournament definition reads like a rulebook. Stage-type functions
(`round_robin`, `single_elim`, `swiss`, …) are the **verbs** — pipe them
onto `tournament()` or `spec()` to build the graph.

A tournament runtime feels like a scoreboard. Noun functions (`matches`,
`standings`, `stage_status`, `winner`, …) expose live state without
mutating it.

---

## 1) Define

### `tournament()` and `spec()`

`tournament(participants)` is the entry point for a live runtime. It
returns a `tournament` object that you pipe stage verbs onto.

`spec()` is the entry point for a reusable blueprint without participants.
It returns a `bracketeer_spec` object; use `build(spec, participants)` to
materialize it later.

```{r}
teams <- paste("Team", LETTERS[1:16])

# ── Live tournament ───────────────────────────────────────────────────────────
trn <- tournament(teams) |>
  round_robin("groups")

# ── Blueprint / reusable spec ─────────────────────────────────────────────────
my_spec <- spec() |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))
```

`tournament()` and `spec()` accept the same stage verbs. The difference is
that `tournament()` materializes the first stage immediately while `spec()`
defers everything to `build()`.

### Stage verbs

Each stage verb adds one node to the tournament graph. The verb name is
the format; the first argument is a unique stage ID.

| Verb | Format |
|------|--------|
| `round_robin(id, ...)` | All-play-all |
| `single_elim(id, ...)` | Single-elimination bracket |
| `double_elim(id, ...)` | Double-elimination bracket |
| `swiss(id, ...)` | Swiss-system |
| `two_leg(id, ...)` | Two-leg knockout |
| `group_stage_knockout(id, ...)` | Combined group + knockout |

Every verb accepts two wiring parameters:

- `from` — source stage ID (default `previous_stage()`).
- `take` — routing selector (default: all participants from source).

### `from =` and `previous_stage()`

In a linear chain, `from` defaults to the immediately preceding stage. You
never write it explicitly.

```{r}
# These two are identical:
tournament(teams) |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))

tournament(teams) |>
  round_robin("groups") |>
  single_elim("finals", from = previous_stage(), take = top_n(8))
```

For branching — two stages reading from the same source — you must name
`from` explicitly:

```{r}
trn <- tournament(teams) |>
  round_robin("groups") |>
  single_elim("championship", from = "groups", take = top_n(8)) |>
  single_elim("consolation",  from = "groups", take = remaining())
```

### Routing helpers (`take =`)

Pass a selector to `take =` to control who advances from the source stage.

```{r}
top_n(8)               # top 8 by overall standings
bottom_n(4)            # bottom 4
slice_range(5, 8)      # positions 5 through 8
remaining()            # everyone not already consumed by a prior transition
losers()               # eliminated participants
filter_by(function(df, ...) df[df$points >= 6, ])  # custom predicate

# ── Per-group variants (require grouped source stage) ─────────────────────────
top_per_group(2)       # top 2 from each group
bottom_per_group(1)    # bottom 1 from each group
slice_per_group(3, 3)  # 3rd place from each group
```

---

## 2) Validate (spec path)

`validate(spec, n)` runs preflight feasibility checks before materializing
anything.

```{r}
my_spec <- spec() |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))

validate(my_spec, n = 16)   # passes
validate(my_spec, n = 6)    # fails — can't select top 8 from 6 teams
```

Errors are identifier-rich: they include stage IDs and participant counts.
`build()` implicitly calls `validate()` before materializing.

---

## 3) Build (spec path)

```{r}
my_spec <- spec() |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))

trn <- my_spec |> build(teams)
```

`build()` materializes the first stage immediately and leaves downstream
stages in `"blocked"` state until transitions resolve.

---

## 4) Enter results

### `result()` — single match

```{r}
trn <- trn |> result("groups", match = 1, score = c(2, 1))
```

- `stage` — stage ID.
- `match` — match ID from `matches()`.
- `score` — length-2+ numeric vector. For best-of series, a per-game
  score vector (e.g. `c(3, 1, 2, 0, 3)` for a 5-game series).

### `results()` — batch from data frame

```{r}
results_df <- data.frame(
  match  = 1:4,
  score1 = c(2, 1, 3, 0),
  score2 = c(1, 0, 2, 1)
)

trn <- trn |> results("groups", results_df)
```

Columns must be `match`, `score1`, `score2`. Batch entry triggers
auto-advance when the final result completes the stage.

### Auto-advance

By default, entering the last match in a stage automatically materializes
all downstream stages — no explicit `advance()` call needed.

```{r}
# All group matches entered → finals stage materializes automatically.
group_ms <- matches(trn, "groups")

for (i in seq_len(nrow(group_ms))) {
  trn <- trn |> result("groups", match = group_ms$id[i], score = c(1, 0))
}

stage_status(trn)  # finals is now "active"
```

### Manual advance (opt-in)

```{r}
trn <- tournament(teams, auto_advance = FALSE) |>
  round_robin("groups") |>
  single_elim("finals", take = top_n(8))

# ... enter results ...

trn <- trn |> advance("groups")  # explicit trigger
```

### Overwrite policy

- Overwriting a result **before** downstream stages materialize is allowed;
  standings recalculate automatically.
- Overwriting a result **after** downstream materialization is blocked with
  an error that identifies the blocking downstream stage ID.
- Use `teardown()` to un-materialize downstream stages and unlock the
  upstream result for editing.

---

## 5) Inspect state

All inspection functions return plain data frames or scalar values.

```{r}
# ── Print ───────────────────────────────────────────────────────────────────
trn
# Tournament [2 stages]
#   groups   in_progress   90/120 matches
#   finals   blocked

# ── Stage overview ──────────────────────────────────────────────────────────
stage_status(trn)
#   stage    status        complete  total  materialized
#   groups   in_progress         90    120          TRUE
#   finals   blocked              0      0         FALSE

# ── Matches ─────────────────────────────────────────────────────────────────
matches(trn)                           # all stages, pending (default)
matches(trn, "groups")                 # one stage, pending
matches(trn, "groups", status = "all") # one stage, all

# ── Standings ───────────────────────────────────────────────────────────────
standings(trn, "groups")   # one stage
standings(trn)              # all stages

# ── Outcomes ────────────────────────────────────────────────────────────────
winner(trn)       # tournament winner (NA until complete)
rankings(trn)     # final placement table
routing_log(trn)  # transition audit trail
```

---

## 6) Teardown

`teardown(trn, stage)` un-materializes a stage and all downstream
dependents, cascading along the DAG. Source-stage results and standings
are preserved.

```{r}
trn <- tournament(teams) |>
  swiss("open", rounds = 5) |>
  single_elim("top_cut", take = top_n(8))

# ... enter all open results, top_cut materializes automatically ...

# Decide to revise an open result:
trn <- teardown(trn, "top_cut")     # un-materializes top_cut
trn <- result(trn, "open", match = 3, score = c(0, 2))  # overwrite now allowed
# top_cut will re-materialize when open is completed again.
```

After teardown, torn-down stages revert to `"blocked"` in `stage_status()`.

---

## 7) Full lifecycle example

```{r}
library(bracketeer)

teams <- paste("Team", LETTERS[1:16])

# ── Define ────────────────────────────────────────────────────────────────────
trn <- tournament(teams) |>
  swiss("open", rounds = 5) |>
  single_elim("top_cut", take = top_n(8))

# ── Enter results ─────────────────────────────────────────────────────────────
open_ms <- matches(trn, "open")

for (i in seq_len(nrow(open_ms))) {
  trn <- trn |> result("open", match = open_ms$id[i], score = c(1, 0))
}

# top_cut auto-materialized after final open result.

# ── Inspect ───────────────────────────────────────────────────────────────────
stage_status(trn)
matches(trn, "top_cut")
standings(trn, "open")

# ── Run top_cut ───────────────────────────────────────────────────────────────
cut_ms <- matches(trn, "top_cut")

for (i in seq_len(nrow(cut_ms))) {
  trn <- trn |> result("top_cut", match = cut_ms$id[i], score = c(1, 0))
}

# ── Outcomes ──────────────────────────────────────────────────────────────────
winner(trn)
rankings(trn)
routing_log(trn)
```
