#!/usr/bin/env python3
"""JavaScript Production Readiness Checklist

A vendor-neutral Python 3 script for operational teams to run a structured
production readiness review for JavaScript codebases.

Features:
- Interactive or command-line use
- Readiness scoring across five phases
- Evidence capture placeholders
- Clear pass/fail/partial handling
- No external dependencies
- No hard-coded secrets or environment-specific endpoints

Usage examples:
    python js_readiness_checklist.py
    python js_readiness_checklist.py --interactive
    python js_readiness_checklist.py --scores 2 2 1 2 1
    python js_readiness_checklist.py --output review-summary.md

Scoring:
    2 = all checks pass and evidence is complete
    1 = most checks pass, but one non-critical item needs follow-up
    0 = one or more checks fail, or evidence is missing

Interpretation:
    10-12 = Ready for production with normal change control
    7-9   = Conditionally ready; approve only with documented follow-up actions
    0-6   = Not ready; do not release until failures are resolved
"""

from __future__ import annotations

import argparse
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, Sequence


PHASES = [
    {
        "name": "Scope and runtime assumptions",
        "owner": "application engineer or platform engineer",
        "cadence": "every release and after any runtime upgrade",
        "items": [
            "Confirm the code path is explicitly tied to the correct runtime: browser, Node.js, edge runtime, serverless function, or test-only utility.",
            "Review module format assumptions and verify whether the package expects ESM, CommonJS, or dual packaging.",
            "Validate that all runtime-specific APIs are supported in the deployed environment and version range.",
            "Document any required flags, polyfills, transpilation targets, or language features that affect behavior.",
            "Assign a named owner for runtime compatibility decisions and version upgrades.",
            "Review the change cadence for runtime versions, browser support policy, and dependency update windows.",
        ],
        "evidence": "package metadata, runtime matrix, supported platform list, build configuration, feature flag list, and compatibility notes",
        "common_mistakes": [
            "Mixing browser-only globals into server code.",
            "Relying on syntax unsupported by the actual production target.",
            "Assuming local development behavior matches production runtime flags.",
        ],
    },
    {
        "name": "Source quality and maintainability",
        "owner": "code author with peer reviewer approval",
        "cadence": "each pull request and before release candidates",
        "items": [
            "Review function size and complexity and confirm the code is split into testable units.",
            "Validate variable names, parameter names, and return values for clarity and consistency.",
            "Confirm error handling paths are explicit and do not suppress failures without justification.",
            "Document any intentional tradeoffs such as compatibility shims, temporary duplication, or guarded exceptions.",
            "Assign a reviewer to verify that dead code, debugging statements, and commented-out logic are removed.",
            "Test the primary execution paths and failure paths with realistic input values.",
        ],
        "evidence": "static analysis output, code review comments, test results, and a short design note for any non-obvious implementation choice",
        "common_mistakes": [
            "Leaving broad catch blocks that hide root causes.",
            "Using nested callbacks or deeply chained promises where a simpler structure would be safer.",
            "Allowing implicit coercion to create hard-to-read behavior.",
        ],
    },
    {
        "name": "Dependency and supply-chain control",
        "owner": "application engineer plus security or supply-chain reviewer",
        "cadence": "every dependency change and on a fixed periodic review cycle",
        "items": [
            "Review direct and transitive dependencies and confirm each package has a clear business or technical need.",
            "Validate lockfile consistency and verify the committed lockfile matches the intended dependency graph.",
            "Confirm version ranges are deliberate and do not permit uncontrolled upgrades in production builds.",
            "Document any known package exceptions, forks, or vendored code that affect supportability.",
            "Assign ownership for dependency review, including security alerts and patch cadence.",
            "Test install and build behavior from a clean environment to confirm reproducible results.",
        ],
        "evidence": "lockfile, dependency inventory, package audit results, clean install logs, and approval notes for exceptions",
        "common_mistakes": [
            "Accepting broad semver ranges without understanding upgrade impact.",
            "Shipping packages that are no longer maintained without mitigation.",
            "Reviewing only direct dependencies and ignoring transitive risk.",
        ],
    },
    {
        "name": "Security controls and input handling",
        "owner": "security engineer or application engineer with security sign-off",
        "cadence": "every security-sensitive change and after any input surface expansion",
        "items": [
            "Review all input sources and confirm validation happens before the data reaches business logic.",
            "Validate that output encoding or escaping is applied in the correct context, especially for HTML, attributes, URLs, and logs.",
            "Confirm secrets, tokens, and session values are never written to client-visible storage unless there is a documented design requirement.",
            "Document any use of dynamic code execution, template injection risk, or runtime evaluation and prove why it is necessary.",
            "Assign a security reviewer to confirm authorization checks are server-side and not only enforced in the client.",
            "Test negative cases for malformed input, oversized payloads, missing fields, and tampered requests.",
        ],
        "evidence": "validation rules, security review notes, negative test results, and examples of encoded output or rejected payloads",
        "common_mistakes": [
            "Trusting client-side validation as a security boundary.",
            "Logging secrets or session identifiers during debug sessions.",
            "Using dynamic code paths when static alternatives exist.",
        ],
    },
    {
        "name": "Error handling, logging, and observability",
        "owner": "application engineer with operations review",
        "cadence": "every release and after observability changes",
        "items": [
            "Confirm errors are actionable, classified where appropriate, and do not leak sensitive details.",
            "Validate logs contain enough context to diagnose failures without exposing secrets or personal data.",
            "Ensure metrics, traces, alerts, and dashboards cover the critical runtime paths.",
            "Document retry, backoff, timeout, and circuit-breaking behavior where applicable.",
            "Assign ownership for alert routing and incident response escalation.",
            "Test a failure scenario and verify operators can detect, trace, and explain it.",
        ],
        "evidence": "sample logs, alert rules, dashboards, trace samples, failure test results, and on-call ownership notes",
        "common_mistakes": [
            "Swallowing exceptions and returning ambiguous failures.",
            "Logging too little context to diagnose production issues.",
            "Logging too much context, including secrets or identifiers that should remain private.",
        ],
    },
]


@dataclass
class ReviewResult:
    scores: List[int]
    total: int
    status: str


def clamp_score(value: int) -> int:
    if value not in (0, 1, 2):
        raise argparse.ArgumentTypeError("Scores must be 0, 1, or 2.")
    return value


def determine_status(total: int) -> str:
    if total >= 10:
        return "Ready for production with normal change control"
    if total >= 7:
        return "Conditionally ready; approve only with documented follow-up actions and owner dates"
    return "Not ready; do not release until failures are resolved"


def prompt_score(phase_name: str) -> int:
    while True:
        raw = input(f"Score '{phase_name}' (0, 1, or 2): ").strip()
        try:
            score = int(raw)
            return clamp_score(score)
        except (ValueError, argparse.ArgumentTypeError):
            print("Please enter 0, 1, or 2.")


def render_report(result: ReviewResult) -> str:
    lines = [
        "# JavaScript Production Readiness Review",
        "",
        f"**Total score:** {result.total} / 10",
        f"**Status:** {result.status}",
        "",
        "## Phase scores",
        "",
    ]
    for idx, phase in enumerate(PHASES):
        score = result.scores[idx]
        lines.append(f"- **{phase['name']}**: {score}/2")
    lines.extend([
        "",
        "## Review notes",
        "",
        "- Record evidence for each phase before approving release.",
        "- Treat any failed item as a release blocker unless a compensating control is documented.",
        "- Update owners and follow-up dates for any conditional approval.",
        "",
        "## Reference guidance",
        "",
        "- Scope and runtime assumptions",
        "- Source quality and maintainability",
        "- Dependency and supply-chain control",
        "- Security controls and input handling",
        "- Error handling, logging, and observability",
    ])
    return "\n".join(lines) + "\n"


def run_interactive() -> ReviewResult:
    print("JavaScript Production Readiness Checklist")
    print("Score each phase from 0 to 2.\n")
    scores = []
    for phase in PHASES:
        print(f"\n=== {phase['name']} ===")
        print(f"Owner: {phase['owner']}")
        print(f"Review cadence: {phase['cadence']}")
        print("Checks:")
        for item in phase["items"]:
            print(f"  - {item}")
        print(f"Evidence to capture: {phase['evidence']}")
        print("Common mistakes:")
        for item in phase["common_mistakes"]:
            print(f"  - {item}")
        scores.append(prompt_score(phase["name"]))
    total = sum(scores)
    return ReviewResult(scores=scores, total=total, status=determine_status(total))


def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="JavaScript production readiness checklist")
    parser.add_argument("--interactive", action="store_true", help="Run in interactive mode")
    parser.add_argument("--scores", nargs=5, type=clamp_score, metavar=("S1", "S2", "S3", "S4", "S5"), help="Provide five phase scores (0, 1, or 2)")
    parser.add_argument("--output", type=Path, help="Write a markdown summary to this file")
    return parser.parse_args(argv)


def main(argv: Optional[Sequence[str]] = None) -> int:
    args = parse_args(argv)

    if args.interactive or not args.scores:
        result = run_interactive()
    else:
        scores = list(args.scores)
        total = sum(scores)
        result = ReviewResult(scores=scores, total=total, status=determine_status(total))

    report = render_report(result)
    print("\n" + report)

    if args.output:
        args.output.write_text(report, encoding="utf-8")
        print(f"Summary written to {args.output}")

    return 0


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