"""Minimal AWS Signature V4 for S3-compatible endpoints — stdlib only.

Just enough for the self-host publisher: path-style PUT/GET/DELETE of
objects (plus PutBucketPolicy's ?policy= query). boto3 would drag ~80MB of
botocore into every install for a feature most users never touch.
"""
import datetime
import hashlib
import hmac
import urllib.parse
import urllib.request


def _uri_encode(s: str, keep_slash: bool) -> str:
    safe = "/-_.~" if keep_slash else "-_.~"
    return urllib.parse.quote(s, safe=safe)


def _hmac(key: bytes, msg: str) -> bytes:
    return hmac.new(key, msg.encode(), hashlib.sha256).digest()


def request(method: str, endpoint: str, bucket: str, key: str,
            access_key: str, secret_key: str, body: bytes = b"",
            query: dict | None = None, region: str = "auto",
            content_type: str = "", timeout: int = 300):
    """Signed S3 request. Returns the urlopen response (caller reads/closes).
    Raises urllib.error.HTTPError on non-2xx."""
    u = urllib.parse.urlparse(endpoint)
    host = u.netloc
    path = f"{u.path.rstrip('/')}/{bucket}/{key}".replace("//", "/")
    if not path.startswith("/"):
        path = "/" + path

    now = datetime.datetime.now(datetime.timezone.utc)
    amz_date = now.strftime("%Y%m%dT%H%M%SZ")
    datestamp = now.strftime("%Y%m%d")
    payload_hash = hashlib.sha256(body).hexdigest()

    q = query or {}
    canonical_query = "&".join(
        f"{_uri_encode(k, False)}={_uri_encode(str(v), False)}"
        for k, v in sorted(q.items()))

    headers = {"host": host, "x-amz-content-sha256": payload_hash,
               "x-amz-date": amz_date}
    if content_type:
        headers["content-type"] = content_type
    signed_headers = ";".join(sorted(headers))
    canonical_headers = "".join(f"{k}:{headers[k].strip()}\n" for k in sorted(headers))

    canonical_request = "\n".join([
        method, _uri_encode(path, True), canonical_query,
        canonical_headers, signed_headers, payload_hash])
    scope = f"{datestamp}/{region}/s3/aws4_request"
    string_to_sign = "\n".join([
        "AWS4-HMAC-SHA256", amz_date, scope,
        hashlib.sha256(canonical_request.encode()).hexdigest()])
    key_bytes = _hmac(_hmac(_hmac(_hmac(
        ("AWS4" + secret_key).encode(), datestamp), region), "s3"), "aws4_request")
    signature = hmac.new(key_bytes, string_to_sign.encode(), hashlib.sha256).hexdigest()

    url = f"{u.scheme}://{host}{path}"
    if canonical_query:
        url += "?" + canonical_query
    req_headers = {k: v for k, v in headers.items() if k != "host"}
    req_headers["Authorization"] = (
        f"AWS4-HMAC-SHA256 Credential={access_key}/{scope}, "
        f"SignedHeaders={signed_headers}, Signature={signature}")
    req = urllib.request.Request(url, data=body if method in ("PUT", "POST") else None,
                                 headers=req_headers, method=method)
    return urllib.request.urlopen(req, timeout=timeout)


def put(endpoint, bucket, key, body, access_key, secret_key,
        content_type="application/octet-stream", region="auto"):
    with request("PUT", endpoint, bucket, key, access_key, secret_key,
                 body=body, content_type=content_type, region=region) as r:
        return r.status


def delete(endpoint, bucket, key, access_key, secret_key, region="auto"):
    try:
        with request("DELETE", endpoint, bucket, key, access_key, secret_key,
                     region=region) as r:
            return r.status
    except Exception:
        return None


def get(endpoint, bucket, key, access_key, secret_key, region="auto") -> bytes:
    with request("GET", endpoint, bucket, key, access_key, secret_key,
                 region=region) as r:
        return r.read()
