```python
#!/usr/bin/env python3
"""secure_api_auth_readiness.py

Assess operational readiness for secure API authentication in a machine learning
model deployment.

This script is intentionally vendor-neutral. It does not connect to any external
systems, does not require secrets, and performs no destructive actions.

Use it to:
- review whether a model API has the expected controls in place
- identify likely gaps before production
- produce a concise readiness summary for engineering or security review

Examples:
    python secure_api_auth_readiness.py --interactive
    python secure_api_auth_readiness.py --answers answers.json
    python secure_api_auth_readiness.py --answers answers.json --report report.txt
"""

from __future__ import annotations

import argparse
import json
import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple


CONTROL_ORDER = [
    "authentication",
    "authorization",
    "transport_security",
    "rate_limiting",
    "logging",
    "revocation_rotation",
    "input_validation",
    "anomaly_detection",
    "incident_response",
    "production_readiness",
]


PROMPTS = {
    "authentication": "Is every request authenticated before inference begins?",
    "authorization": "Is access scoped to the smallest practical set of endpoints, models, tenants, or roles?",
    "transport_security": "Is TLS enforced for all client connections and internal hops that carry credentials?",
    "rate_limiting": "Are rate limits or quotas applied to prevent abuse and noisy traffic?",
    "logging": "Are authentication decisions and request correlation IDs logged without exposing secrets?",
    "revocation_rotation": "Can credentials be revoked or rotated without breaking unrelated clients?",
    "input_validation": "Are requests validated before model execution to reduce malformed or abusive input?",
    "anomaly_detection": "Is there monitoring for credential misuse, scraping patterns, or unusual inference spikes?",
    "incident_response": "Is there a documented process to respond to exposed or compromised credentials?",
    "production_readiness": "Can the team explain the trade-offs, ownership, and lifecycle for the chosen auth pattern?",
}


@dataclass
class AssessmentResult:
    answers: Dict[str, bool] = field(default_factory=dict)
    score: int = 0
    total: int = 0
    missing: List[str] = field(default_factory=list)
    gaps: List[str] = field(default_factory=list)
    strengths: List[str] = field(default_factory=list)
    recommendations: List[str] = field(default_factory=list)


def parse_bool(value: Any) -> bool:
    if isinstance(value, bool):
        return value
    if isinstance(value, (int, float)) and value in (0, 1):
        return bool(value)
    if isinstance(value, str):
        normalized = value.strip().lower()
        if normalized in {"y", "yes", "true", "1", "on"}:
            return True
        if normalized in {"n", "no", "false", "0", "off"}:
            return False
    raise ValueError(f"Invalid boolean value: {value!r}")


def load_answers(path: Path) -> Dict[str, bool]:
    if not path.exists():
        raise FileNotFoundError(f"Answers file not found: {path}")
    raw = json.loads(path.read_text(encoding="utf-8"))
    if not isinstance(raw, dict):
        raise ValueError("Answers file must contain a JSON object mapping control names to booleans.")

    answers: Dict[str, bool] = {}
    for key, value in raw.items():
        if key not in CONTROL_ORDER:
            continue
        answers[key] = parse_bool(value)
    return answers


def ask_interactive() -> Dict[str, bool]:
    answers: Dict[str, bool] = {}
    print("Secure API Authentication Readiness Review\n")
    for key in CONTROL_ORDER:
        prompt = PROMPTS[key] + " [y/n]: "
        while True:
            try:
                response = input(prompt)
                answers[key] = parse_bool(response)
                break
            except ValueError:
                print("Please answer y or n.")
    return answers


def evaluate(answers: Dict[str, bool]) -> AssessmentResult:
    result = AssessmentResult(answers=answers)
    result.total = len(CONTROL_ORDER)

    for key in CONTROL_ORDER:
        if key not in answers:
            result.missing.append(key)
            continue
        if answers[key]:
            result.score += 1
            result.strengths.append(key)
        else:
            result.gaps.append(key)

    if not answers.get("authentication", False):
        result.recommendations.append("Authenticate every request before model execution.")
    if not answers.get("authorization", False):
        result.recommendations.append("Scope authorization to the smallest practical set of resources and actions.")
    if not answers.get("transport_security", False):
        result.recommendations.append("Enforce TLS for all API traffic and protect credentials in transit.")
    if not answers.get("rate_limiting", False):
        result.recommendations.append("Add rate limits or quotas to reduce abuse and protect inference capacity.")
    if not answers.get("logging", False):
        result.recommendations.append("Log authentication outcomes with correlation IDs and avoid sensitive data in logs.")
    if not answers.get("revocation_rotation", False):
        result.recommendations.append("Ensure credentials can be revoked or rotated without impacting unrelated clients.")
    if not answers.get("input_validation", False):
        result.recommendations.append("Validate inputs before inference to reduce malformed or abusive requests.")
    if not answers.get("anomaly_detection", False):
        result.recommendations.append("Monitor for unusual access patterns, scraping, or credential misuse.")
    if not answers.get("incident_response", False):
        result.recommendations.append("Document a response plan for exposed or compromised credentials.")
    if not answers.get("production_readiness", False):
        result.recommendations.append("Document ownership, rotation, revocation, and the operational trade-offs of the chosen auth pattern.")

    return result


def classify(score: int, total: int) -> str:
    if total <= 0:
        return "Unknown"
    ratio = score / total
    if ratio >= 0.9:
        return "Production-ready with minor follow-up items"
    if ratio >= 0.7:
        return "Promising, but review gaps before production"
    if ratio >= 0.5:
        return "Needs significant hardening"
    return "Not ready for production"


def render_report(result: AssessmentResult) -> str:
    lines: List[str] = []
    lines.append("Secure API Authentication Readiness Report")
    lines.append("=" * 44)
    lines.append(f"Score: {result.score}/{result.total}")
    lines.append(f"Status: {classify(result.score, result.total)}")
    lines.append("")

    lines.append("Control Summary")
    lines.append("---------------")
    for key in CONTROL_ORDER:
        status = "PASS" if result.answers.get(key, False) else "FAIL"
        lines.append(f"- {key.replace('_', ' ').title()}: {status}")

    lines.append("")
    lines.append("Strengths")
    lines.append("---------")
    if result.strengths:
        for key in result.strengths:
            lines.append(f"- {PROMPTS[key]}")
    else:
        lines.append("- None recorded")

    lines.append("")
    lines.append("Gaps")
    lines.append("----")
    if result.gaps:
        for key in result.gaps:
            lines.append(f"- {PROMPTS[key]}")
    else:
        lines.append("- None recorded")

    lines.append("")
    lines.append("Recommendations")
    lines.append("---------------")
    if result.recommendations:
        for item in result.recommendations:
            lines.append(f"- {item}")
    else:
        lines.append("- No immediate remediation items identified.")

    return "\n".join(lines) + "\n"


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Assess secure API authentication readiness for a machine learning model deployment.",
    )
    parser.add_argument(
        "--answers",
        type=Path,
        help="Path to a JSON file containing boolean answers for the readiness controls.",
    )
    parser.add_argument(
        "--interactive",
        action="store_true",
        help="Run an interactive questionnaire in the terminal.",
    )
    parser.add_argument(
        "--report",
        type=Path,
        help="Optional path to write the text report.",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        help="Print the assessment result as JSON instead of a text report.",
    )

    args = parser.parse_args()

    if args.answers and args.interactive:
        print("Choose either --answers or --interactive, not both.", file=sys.stderr)
        return 2

    try:
        if args.interactive:
            answers = ask_interactive()
        elif args.answers:
            answers = load_answers(args.answers)
        else:
            parser.error("Provide --interactive or --answers.")
            return 2

        result = evaluate(answers)

        output: str
        if args.json:
            output_obj = {
                "score": result.score,
                "total": result.total,
                "status": classify(result.score, result.total),
                "answers": result.answers,
                "missing": result.missing,
                "gaps": result.gaps,
                "recommendations": result.recommendations,
            }
            output = json.dumps(output_obj, indent=2, sort_keys=True)
        else:
            output = render_report(result)

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

        return 0

    except (OSError, ValueError, json.JSONDecodeError) as exc:
        print(f"Error: {exc}", file=sys.stderr)
        return 1


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