"""Characterize EnvDoctor's audit flow at the commit linked in the blog.

Usage: python reading-code-contract.py PATH_TO_ENVDOCTOR_CHECKOUT
Reads the checkout, creates dummy fixtures in a temporary directory, and
does not modify the checkout or contact external services. Python >= 3.12.
"""

import contextlib
import hashlib
import io
import json
from pathlib import Path
import sys
import tempfile

PINNED_COMMIT = "25e43e45683180775d3a8e891bb1bc157ae0222a"
SOURCE_HASHES = {
    "auditor.py": "653ccdaf96e7d3fe680b1d05480415ab864c8fd2c22243f6acef929821a534f6",
    "main.py": "80f22251af0da2e29ca22be7b77930f512c958b969819dd312735d2275814fec",
    "__main__.py": "a8ed74923307354eff87ef94275ffaba1db40c66f9f918e98b392c84c7ca2482",
    "parser.py": "c04e29926ed54f2fd950b78c34ae4f29744d97a7d2b71e8eec5216f61c39fff1",
}
CASES = [
    ("direct", 'import os\nvalue = os.getenv("API_TOKEN")\n', ["API_TOKEN"], [], 0),
    ("dynamic", 'import os\nkey = "API_TOKEN"\nvalue = os.getenv(key)\n', [], [], 0),
    ("alias", 'from os import getenv\nvalue = getenv("API_TOKEN")\n', [], [], 0),
    ("multiline", 'import os\nvalue = os.getenv(\n    "API_TOKEN"\n)\n', [], [], 0),
    ("string-literal", 'text = \'os.getenv("FAKE_KEY")\'\n', ["FAKE_KEY"], ["FAKE_KEY"], 1),
]


def expect(actual, expected):
    if actual != expected:
        raise AssertionError(f"Expected {expected!r}, got {actual!r}")


def run(checkout):
    source = checkout.resolve() / "src"
    for name, expected_hash in SOURCE_HASHES.items():
        normalized = (source / "envdoctor" / name).read_text(encoding="utf-8").replace("\r\n", "\n")
        digest = hashlib.sha256(normalized.encode("utf-8")).hexdigest()
        if digest != expected_hash:
            raise ValueError(f"Source mismatch: {name}; use commit {PINNED_COMMIT}")

    sys.dont_write_bytecode = True
    sys.path.insert(0, str(source))
    from envdoctor.auditor import audit_codebase, scan_file_for_env_keys
    from envdoctor.main import main

    with tempfile.TemporaryDirectory(prefix="envdoctor-reading-") as directory:
        root = Path(directory)
        env_file = root / ".env"
        env_file.write_text("API_TOKEN=demo_not_a_secret\n", encoding="utf-8")
        for name, code, keys, missing, expected_exit in CASES:
            folder = root / name
            folder.mkdir()
            app = folder / "app.py"
            app.write_text(code, encoding="utf-8")
            expect(sorted({r.key for r in scan_file_for_env_keys(app)}), keys)
            result = audit_codebase([folder], {"API_TOKEN"})
            expect(sorted(result.unique_referenced_keys), keys)
            expect(sorted(result.missing_from_env), missing)
            expect(sorted(result.stale_in_env), sorted({"API_TOKEN"} - set(keys)))
            output = io.StringIO()
            with contextlib.redirect_stdout(output):
                exit_code = main(["--format", "json", "audit", str(folder), "--env", str(env_file)])
            report = json.loads(output.getvalue())
            expect(report["missing_from_env"], missing)
            expect(report["referenced_keys_count"], len(keys))
            expect(exit_code, expected_exit)
            print(json.dumps({"case": name, "detected": keys, "missing": missing, "exit": exit_code}))

        ignored_root = root / "ignored"
        ignored = ignored_root / "node_modules"
        ignored.mkdir(parents=True)
        (ignored / "app.py").write_text('value = os.getenv("IGNORED_KEY")\n', encoding="utf-8")
        expect(len(audit_codebase([ignored_root], set()).references), 0)
        # An explicitly supplied file is scanned even inside an ignored directory.
        expect(sorted(audit_codebase([ignored / "app.py"], set()).unique_referenced_keys), ["IGNORED_KEY"])
    print("PASS: 5 scanner/report/CLI cases and directory-vs-explicit-file boundary")


if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python reading-code-contract.py PATH_TO_ENVDOCTOR_CHECKOUT")
    run(Path(sys.argv[1]))
