# Multi-line conn refactoring

Extend `refactor_conn_aliasing.sh` (or replace with an Elixir tool) to handle multi-line `conn = verb(conn, ...)` patterns that the current script misses.

## Current state

The script handles **single-line** triggers:

```elixir
conn = get(conn, ~p"/path")
```

It misses **multi-line** triggers (11 remaining credo issues):

```elixir
conn =
  post(conn, ~p"/users/log-in", %{
    "user" => %{"email" => user.email, "password" => "pass"}
  })
```

## Multi-line trigger shapes

Two forms observed in the codebase:

### Form A — verb on next line

```elixir
conn =
  post(conn, ~p"/users/log-in", %{
    "user" => %{"email" => user.email}
  })
```

`conn =` ends the line; the verb call starts on the next line with deeper indentation.

### Form B — verb on same line, args span multiple lines

```elixir
conn = post(conn, ~p"/users/log-in", %{
  "user" => %{"email" => user.email}
})
```

`conn = verb(conn,` starts on one line but the arguments (maps, keyword lists) span multiple lines.

## Transformations

The same four cases apply as for single-line, just with multi-line args preserved:

### Case 1 — assign + helper on next line

```elixir
# Before
conn =
  post(conn, ~p"/users/log-in", %{
    "user" => %{"email" => user.email}
  })
response = html_response(conn, 200)

# After
response = conn |> post(~p"/users/log-in", %{
  "user" => %{"email" => user.email}
}) |> html_response(200)
```

### Case 2 — assert helper on next line

```elixir
# Before
conn =
  post(conn, ~p"/users/log-in", %{
    "user" => %{"email" => user.email}
  })
assert html_response(conn, 200) =~ "Welcome"

# After
assert conn |> post(~p"/users/log-in", %{
  "user" => %{"email" => user.email}
}) |> html_response(200) =~ "Welcome"
```

### Case 3 — assert pattern match + helper

Same as Case 2 but with `assert %{body: body} = html_response(conn, 200)`.

### Case 4 — multiple conn references ahead

```elixir
# Before
conn =
  post(conn, ~p"/users/log-in", %{
    "user" => %{"email" => user.email}
  })
assert redirected_to(conn) == ~p"/"
assert get_session(conn, :user_token)

# After
response = conn |> post(~p"/users/log-in", %{
  "user" => %{"email" => user.email}
})
assert redirected_to(response) == ~p"/"
assert get_session(response, :user_token)
```

## Algorithm

### Phase 1 — Detect trigger start

Match either:
- `conn =` at end of line (Form A, verb on next line)
- `conn = verb(conn,` where the line does not end with a balanced `)` (Form B, args continue on next line)

### Phase 2 — Accumulate the expression

Read subsequent lines until the expression is complete. Track parenthesis/brace/bracket depth to find the closing boundary:

- Start with depth from the trigger line (e.g., `post(conn, ~p"/path", %{` has depth 2: one `(`, one `{`)
- For each subsequent line, update depth by counting opening vs closing delimiters
- When depth reaches 0, the expression is complete

Buffer all accumulated lines.

### Phase 3 — Extract verb and args

From the accumulated block:
- **verb**: the HTTP verb (`get`, `post`, `put`, `patch`, `delete`, `head`, `options`)
- **args**: everything between `verb(conn,` and the final `)`, preserving original formatting (indentation, newlines)

### Phase 4 — Peek ahead for case classification

Skip blank lines after the accumulated expression. Read the next non-blank line (`next_line`).

Classify as Case 1, 2, or 3 using the same patterns as today (helper assignment, assert helper, assert pattern match).

Then **look ahead further** (skipping blanks, stopping at `end`, `conn =`, `test`, `describe`) to check if `conn` is referenced again. If so, override to Case 4.

### Phase 5 — Emit output

- **Cases 1–3**: emit a single merged pipeline line, inserting the multi-line args as-is:
  ```
  indent + var + " = conn |> " + verb + "(" + args + ") |> " + helper + "(" + status + ")"
  ```
  For multi-line args, the output will naturally span multiple lines.

- **Case 4**: emit `response = conn |> verb(args)`, then rename `conn` → `response` in subsequent lines until scope boundary (`end`, `conn =`, `test`, `describe`).

- **Fallback** (no conn on next line, no recognized pattern): emit the original trigger unchanged.

## Remaining issues to handle

| File | Line | Verb |
|---|---|---|
| `user_settings_controller_test.exs` | 74 | `put` |
| `user_settings_controller_test.exs` | 89 | `put` |
| `user_session_controller_test.exs` | 76 | `post` |
| `user_session_controller_test.exs` | 88 | `post` |
| `user_session_controller_test.exs` | 119 | `post` |
| `user_session_controller_test.exs` | 132 | `post` |
| `user_session_controller_test.exs` | 144 | `post` |
| `user_session_controller_test.exs` | 157 | `post` |
| `user_session_controller_test.exs` | 171 | `post` |
| `user_registration_controller_test.exs` | 44 | `post` |

All in `app/test/firehose_web/controllers/`.

## Elixir implementation notes (for future session)

An Elixir implementation would be simpler than the awk script because:

- Read file into a list of lines (already a list data structure)
- Pattern match on line content with guards
- Recurse or use `reduce_while` over the line list with explicit state (`:normal`, `:accumulating`, `:triggered`, `:renaming`)
- String manipulation with `String.split/2`, `String.replace/3`, etc.
- No need for regex capture groups — use `String.starts_with?/2`, `String.contains?/2`, etc.
- Parenthesis depth tracking is a simple integer accumulator

Possible structure:

```elixir
defmodule ConnRefactor do
  def refactor(file_path) do
    file_path
    |> File.read!()
    |> String.split("\n", trim: false)
    |> do_refactor([], :normal, %{})
    |> Enum.join("\n")
  end

  defp do_refactor(lines, output, state, context)
end
```
