#!/usr/bin/env python3
"""Five read-only GET requests to one public text file; no browser cache test.

Usage: python3 http304-lab.py [output-directory]
Requires Python 3.9+ and curl 7.68+; no Python packages are required.
"""

from datetime import datetime
import hashlib
import json
from pathlib import Path
import platform
import subprocess
import sys
import tempfile

URL = "https://gumi.ink/examples/great-wave-reading-notes.txt"


def request(work, label, options, stored_body_available):
    body = work / (label + ".body")
    headers = work / (label + ".headers")
    command = ["curl", "--http1.1", "--silent", "--show-error", "--fail",
               "--max-time", "20", "--dump-header", headers.name,
               "--output", body.name,
               "--write-out", "%{http_code}\n%{size_download}\n%{http_version}\n",
               *options, URL]
    result = subprocess.run(command, cwd=work, text=True, capture_output=True, check=True)
    status, downloaded, version = result.stdout.strip().splitlines()
    raw = headers.read_text(encoding="iso-8859-1")
    # A CONNECT proxy can prepend another HTTP header block. Keep the last one.
    blocks = [part for part in raw.split("\n\n") if part.startswith("HTTP/")]
    final_header = blocks[-1]
    fields = {}
    for line in final_header.splitlines()[1:]:
        if ":" in line:
            key, value = line.split(":", 1)
            fields[key.lower()] = value.strip()
    data = body.read_bytes() if body.exists() else b""
    assert len(data) == int(float(downloaded)), (label, downloaded, len(data))
    return {"case": label, "request_options": options, "method": "GET",
            "status": int(status), "http_version": version,
            "downloaded_body_bytes": len(data), "output_file_exists": body.exists(),
            "body_sha256": hashlib.sha256(data).hexdigest() if data else None,
            "stored_body_available_before_request": stored_body_available,
            "response_headers": fields, "raw_final_response_header": final_header}


def main():
    output = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("http304-output")
    output.mkdir(parents=True, exist_ok=True)
    curl_version = subprocess.run(["curl", "--version"], text=True,
                                  capture_output=True, check=True).stdout.splitlines()[0]
    with tempfile.TemporaryDirectory(prefix="gumi-http304-") as temporary:
        work = Path(temporary)
        first = request(work, "unconditional", ["--etag-save", "etag.txt"], False)
        assert first["status"] == 200 and first["downloaded_body_bytes"] > 0
        tag = (work / "etag.txt").read_text().strip()
        assert tag and tag == first["response_headers"].get("etag")
        same = request(work, "matching_tag", ["--etag-compare", "etag.txt"], True)
        wrong_tag = '"gumi-http304-lab-nonmatching"'
        assert wrong_tag != tag
        wrong = request(work, "nonmatching_tag", ["--header", "If-None-Match: " + wrong_tag], True)
        weak_tag = tag if tag.startswith("W/") else "W/" + tag
        weak = request(work, "weak_matching_tag", ["--header", "If-None-Match: " + weak_tag], True)
        (work / "unconditional.body").unlink()
        assert not (work / "unconditional.body").exists() and (work / "etag.txt").is_file()
        missing = request(work, "tag_without_stored_body", ["--etag-compare", "etag.txt"], False)
        rows = [first, same, wrong, weak, missing]
        report = {
            "recorded_at": datetime.now().astimezone().isoformat(), "url": URL,
            "environment": {"python": platform.python_version(), "system": platform.system(),
                            "macos": platform.mac_ver()[0], "machine": platform.machine(),
                            "curl": curl_version},
            "initial_etag": tag, "requests": rows,
            "scope": "Five sequential read-only GET requests to one URL. The missing-body case deliberately deletes only the first curl output in a temporary directory. No server changes, browser cache inspection, CDN topology inspection or load tests.",
        }
        (output / "results.json").write_text(json.dumps(report, ensure_ascii=False, indent=2) + "\n",
                                             encoding="utf-8")
        assert same["status"] == weak["status"] == missing["status"] == 304
        assert all(row["downloaded_body_bytes"] == 0 for row in (same, weak, missing))
        assert wrong["status"] == 200 and wrong["body_sha256"] == first["body_sha256"]
        print(json.dumps({"environment": report["environment"], "etag": tag,
                          "cases": [{key: row[key] for key in
                                     ("case", "status", "downloaded_body_bytes",
                                      "stored_body_available_before_request", "output_file_exists")}
                                    for row in rows]}, indent=2))


if __name__ == "__main__":
    main()
