"""`ablate init` — the five-minute path. docs/04-task-suite.md."""

from __future__ import annotations

from pathlib import Path

from ablate.errors import AblateError

_TEST_COMMANDS: list[tuple[str, str]] = [
    ("pytest.ini", "uv run pytest -q"),
    ("pyproject.toml", "uv run pytest -q"),
    ("setup.py", "python -m pytest -q"),
    ("package.json", "npm test"),
    ("Cargo.toml", "cargo test"),
    ("go.mod", "go test ./..."),
    ("Makefile", "make test"),
]

_CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".cursorrules"]


def detect_test_command(repo: Path) -> str | None:
    for marker, cmd in _TEST_COMMANDS:
        if (repo / marker).exists():
            return cmd
    return None


def detect_config_file(repo: Path) -> str | None:
    for name in _CONFIG_FILES:
        if (repo / name).exists():
            return name
    return None


def _template_tasks(test_cmd: str, config_file: str | None) -> dict[str, str]:
    del config_file  # not yet used in the template bodies; kept for signature stability
    return {
        "bug-fix.yaml": f"""\
id: bug-fix
description: Fix a real bug and add a regression test. TODO fill in the specifics.

# base_commit: <pin this to a commit where the bug is reproducible>
prompt: |
  TODO: describe a real, reproducible bug in this repo, and ask the agent
  to fix it and add a regression test. The archetype that discriminates
  best is a bug with a clear root cause and an existing test suite that
  doesn't yet cover it.

assert:
  - id: tests-pass
    type: exit_code
    run: {test_cmd}
    expect: 0

  - id: regression-test-added
    type: files_touched
    must_include: ["tests/**", "test/**"]
""",
        "convention-adherence.yaml": f"""\
id: convention-adherence
description: >
  Add something new in the repo's existing style. The archetype most
  sensitive to config content — use this one to judge whether your
  CLAUDE.md is actually teaching the agent your idioms.

prompt: |
  TODO: ask the agent to add a new [endpoint / component / CLI command /
  handler] following the existing patterns in this repo.

assert:
  - id: tests-pass
    type: exit_code
    run: {test_cmd}
    expect: 0

  - id: follows-idiom
    type: present
    pattern: 'TODO_REPLACE_WITH_A_REAL_IDIOM_REGEX'
    in: diff
""",
        "restraint.yaml": f"""\
id: restraint
description: >
  A small, narrowly-scoped change. Catches the over-engineering /
  unrequested-refactor failure mode that verbose configs tend to cause.

prompt: |
  TODO: ask for one small, well-specified change — a single bug fix,
  a single flag, a single small function. Be explicit that nothing
  else should be touched.

assert:
  - id: tests-pass
    type: exit_code
    run: {test_cmd}
    expect: 0

  - id: scope-is-small
    type: files_touched
    must_not_include: ["**/*"]  # TODO narrow this to the one file/dir you expect touched
""",
    }


def run_init(repo: Path) -> tuple[str, str | None, list[Path], list[Path]]:
    """Detects the test command and config file, writes .ablate/tasks/*.yaml.

    Existing task files are never overwritten. A hand-written suite is the most
    expensive artifact a user of this tool produces, and a second `ablate init`
    — run to re-check detection, or out of habit — used to replace it with TODO
    stubs silently. `ablate apply` archives every line it removes; init has no
    business being less careful.

    Returns (test_command, config_file, written_paths, skipped_paths).
    """
    test_cmd = detect_test_command(repo)
    if test_cmd is None:
        raise AblateError(
            "could not detect a test command (looked for pytest.ini, pyproject.toml, "
            "setup.py, package.json, Cargo.toml, go.mod, Makefile). Write .ablate/tasks/ "
            "by hand — see docs/04-task-suite.md for the schema."
        )
    config_file = detect_config_file(repo)

    tasks_dir = repo / ".ablate" / "tasks"
    tasks_dir.mkdir(parents=True, exist_ok=True)

    written, skipped = [], []
    for filename, content in _template_tasks(test_cmd, config_file).items():
        path = tasks_dir / filename
        if path.exists():
            skipped.append(path)
            continue
        path.write_text(content)
        written.append(path)

    return test_cmd, config_file, written, skipped
