"""Create the staged Accountable Record source layout.

WHY:
    The staged layout makes the repository pipeline visible in the file tree.
    Each stage is named stage_NNN_concern where NNN is progressive
    pipeline-dependency order and concern is the durable two-letter-coded
    concern (e.g., identity, contract). The number encodes position;
    the concern name and its diagnostic prefix encode identity.
    Position may move; identity must not.

OBS:
    This is a Python replacement scaffolder so it runs on any OS, and can be reused
    to lay out other repos that share the AR pipeline shape.

REQ:
    - Idempotent. Existing files are never overwritten; missing files and
      directories are created.
    - Pure stdlib. No dependencies, so it runs before the project is installed.

ALT:
    A flat layout was rejected: the prior single-package arrangement was too
    large to work on. The staged layout exists specifically to make each
    concern independently workable, one stage at a time.

Usage:
    python tools/scaffold_staged_layout.py            # create under ./src
    python tools/scaffold_staged_layout.py --root DIR # create under DIR/src
    python tools/scaffold_staged_layout.py --dry-run  # show what would be made
"""

import argparse
from pathlib import Path

PACKAGE = "accountable_record"

# Each stage: (number, concern_dir_suffix, step_files).
# The base/ subpackage is added to every stage automatically.
# Step files are the s0NN_*.py within a stage; they carry pipeline-internal
# ordering but live behind the stable public API, so their numbers are an
# implementation detail.
STAGES: tuple[tuple[str, str, tuple[str, ...]], ...] = (
    (
        "010",
        "identity",
        (
            "repository.py",
            "manifest.py",
            "citation.py",
            "versioning.py",
            "validation.py",
        ),
    ),
    (
        "020",
        "contract",
        (
            "context.py",
            "load.py",
            "adapt.py",
            "check.py",
            "validation.py",
        ),
    ),
    (
        "030",
        "sources",
        (
            "model.py",
            "registry.py",
            "discover.py",
            "validation.py",
        ),
    ),
    (
        "040",
        "elements",
        (
            "model.py",
            "registry.py",
            "discover.py",
            "load.py",
            "validation.py",
        ),
    ),
    (
        "050",
        "catalog",
        (
            "model.py",
            "build.py",
            "index.py",
            "validation.py",
            "export.py",
        ),
    ),
    (
        "060",
        "exports",
        (
            "model.py",
            "plan.py",
            "write.py",
            "index.py",
            "validation.py",
        ),
    ),
    (
        "070",
        "resolution",
        (
            "model.py",
            "graph.py",
            "resolve.py",
            "lock.py",
            "verify.py",
        ),
    ),
    (
        "080",
        "generated",
        (
            "model.py",
            "collect.py",
            "compare.py",
            "validation.py",
        ),
    ),
    (
        "090",
        "docs",
        (
            "model.py",
            "plan.py",
            "render.py",
            "index.py",
            "validation.py",
        ),
    ),
)

# Files in the package root (orchestration + shared public surface).
ROOT_FILES: tuple[str, ...] = (
    "__init__.py",
    "py.typed",
    "paths.py",
    "errors.py",
    "diagnostics.py",  # shared Diagnostic / DiagnosticCode types (spine)
    "cli.py",
)

# The orchestration subpackage (Layer 5): registry + runner + commands.
PIPELINE_FILES: tuple[str, ...] = (
    "pipeline/__init__.py",
    "pipeline/stages.py",
    "pipeline/runner.py",
)

COMMAND_FILES: tuple[str, ...] = (
    "commands/__init__.py",
    "commands/run.py",
    "commands/check.py",
    "commands/build_catalog.py",
    "commands/export.py",
    "commands/resolve_packages.py",
    "commands/write_lock.py",
    "commands/verify_lock.py",
    "commands/validate_generated.py",
    "commands/render_docs.py",
)

# Files added inside every stage's base/ subpackage (Layer 0 vocabulary).
STAGE_BASE_FILES: tuple[str, ...] = (
    "base/__init__.py",
    "base/constants.py",
    "base/scalars.py",
    "base/messages.py",
)


def stage_dir_name(number: str, concern: str) -> str:
    """Return the stage directory name, e.g. stage_010_identity."""
    return f"stage_{number}_{concern}"


def planned_files() -> list[str]:
    """Return every relative file path the scaffold should ensure exists."""
    rel: list[str] = []
    rel.extend(ROOT_FILES)
    rel.extend(PIPELINE_FILES)
    rel.extend(COMMAND_FILES)

    for number, concern, step_files in STAGES:
        stage = stage_dir_name(number, concern)
        rel.append(f"{stage}/__init__.py")
        for base_file in STAGE_BASE_FILES:
            rel.append(f"{stage}/{base_file}")
        for step in step_files:
            rel.append(f"{stage}/{step}")

    return rel


def scaffold(root: Path, *, dry_run: bool) -> tuple[int, int]:
    """Create missing directories and files under root/src/PACKAGE.

    Returns:
        (created, skipped) file counts.
    """
    base = root / "src" / PACKAGE
    created = 0
    skipped = 0

    for rel in planned_files():
        path = base / rel
        if path.exists():
            skipped += 1
            continue
        if dry_run:
            print(f"would create: {path}")
            created += 1
            continue
        path.parent.mkdir(parents=True, exist_ok=True)
        path.touch()
        created += 1

    return created, skipped


def main() -> int:
    """Parse CLI args and run the scaffold."""
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--root",
        type=Path,
        default=Path(),
        help="Project root containing (or to contain) the src/ directory.",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Print what would be created without writing anything.",
    )
    args = parser.parse_args()

    created, skipped = scaffold(args.root, dry_run=args.dry_run)
    base = args.root / "src" / PACKAGE
    verb = "Would scaffold" if args.dry_run else "Scaffolded"
    print(f"{verb} staged source layout under {base}")
    print(f"  created: {created}   skipped (already present): {skipped}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
