"""Optional public release metadata for exact Flutter pins; SDK identities remain the official action's."""
import hashlib
import json
import os
from pathlib import Path
import re
import sys
import tempfile

import flutter_download

MAX_BYTES = 16 * 1024 * 1024
EXACT_VERSION = re.compile(r"[0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z][0-9A-Za-z.-]*)?")


def valid_pin(value) -> bool:
    return (isinstance(value, dict) and set(value) == {"version", "channel", "architecture"}
            and isinstance(value["version"], str)
            and bool(EXACT_VERSION.fullmatch(value["version"]))
            and value["channel"] in ("stable", "beta", "dev")
            and value["architecture"] in ("x64", "arm64"))


def context():
    try:
        pin = json.loads(os.environ.get("GOWALK_FLUTTER_RELEASE_PIN", "null"))
        raw = os.environ.get("GOWALK_FLUTTER_RELEASE_CACHE")
        if not raw or not valid_pin(pin):
            return None
        path = Path(raw)
        parent = Path(os.environ["RUNNER_TEMP"]).resolve(strict=True) / "gowalk-flutter-manifests"
        if (path.is_symlink() or path.parent.is_symlink() or path.parent.resolve() != parent
                or not re.fullmatch(r"[a-f0-9]{64}\.json", path.name)):
            return None
        return path, pin
    except (KeyError, OSError, ValueError, TypeError):
        return None


def matches(manifest, pin) -> bool:
    if not isinstance(manifest, dict) or not isinstance(manifest.get("releases"), list):
        return False
    rows = [row for row in manifest["releases"] if isinstance(row, dict)
            and row.get("version") == pin["version"] and row.get("channel") == pin["channel"]
            and (row.get("dart_sdk_arch") or "x64") == pin["architecture"]]
    if len(rows) != 1:
        return False
    row = rows[0]
    return (isinstance(row.get("archive"), str) and not row["archive"].startswith("/")
            and ".." not in row["archive"] and "\\" not in row["archive"]
            and bool(re.fullmatch(r"[a-fA-F0-9]{40}", str(row.get("hash", ""))))
            and bool(re.fullmatch(r"[a-fA-F0-9]{64}", str(row.get("sha256", "")))))


def reuse(url: str, destination: Path, kind: str) -> bool:
    selected = context() if kind == "manifest" else None
    if not selected:
        return False
    path, pin = selected
    try:
        if not path.is_file() or path.stat().st_size > MAX_BYTES:
            return False
        record = json.loads(path.read_bytes())
        if (not isinstance(record, dict) or record.get("schema") != 1 or record.get("url") != url
                or record.get("pin") != pin or not matches(record.get("manifest"), pin)):
            return False
        destination.write_text(json.dumps(record["manifest"]))
        flutter_download.publish(destination, kind)
        print('::notice title=flutter_manifest_cache::{"schema":"gowalk-cicd/flutter-manifest-cache.v1",'
              '"event":"hit"}', file=sys.stderr)
        return True
    except (OSError, ValueError, TypeError):
        return False


def remember(url: str, destination: Path, kind: str) -> None:
    selected = context() if kind == "manifest" else None
    if not selected or destination.stat().st_size > MAX_BYTES:
        return
    path, pin = selected
    temporary = None
    try:
        manifest = json.loads(destination.read_bytes())
        if not matches(manifest, pin):
            return
        content = json.dumps({"schema": 1, "url": url, "pin": pin, "manifest": manifest}).encode()
        if len(content) > MAX_BYTES:
            return
        path.parent.mkdir(mode=0o700, exist_ok=True)
        with tempfile.NamedTemporaryFile(dir=path.parent, prefix="incoming-", delete=False) as output:
            temporary = Path(output.name)
            output.write(content)
        os.replace(temporary, path)
    except (OSError, ValueError, TypeError):
        pass  # This optional cache cannot turn a completed provider read into a failed setup.
    finally:
        if temporary:
            try:
                temporary.unlink(missing_ok=True)
            except OSError:
                pass


def prepare() -> None:
    pin = {"version": os.environ.get("GOWALK_FLUTTER_VERSION", ""),
           "channel": os.environ.get("GOWALK_FLUTTER_CHANNEL", ""),
           "architecture": os.environ.get("GOWALK_FLUTTER_ARCHITECTURE", "").lower()}
    with Path(os.environ["GITHUB_OUTPUT"]).open("a") as output:
        if os.environ.get("GOWALK_FLUTTER_VERSION_FILE") or not valid_pin(pin):
            output.write("enabled=false\n")
            return
        namespace = {"pin": pin, "os": os.environ["RUNNER_OS"],
                     "origin": os.environ.get("FLUTTER_STORAGE_BASE_URL", "https://storage.googleapis.com")}
        digest = hashlib.sha256(json.dumps(namespace, sort_keys=True).encode()).hexdigest()
        path = Path(os.environ["RUNNER_TEMP"]).resolve(strict=True) / "gowalk-flutter-manifests" / f"{digest}.json"
        output.write(f"enabled=true\nkey=flutter-manifest-v1-{digest}\npath={path}\npin={json.dumps(pin)}\n")


if __name__ == "__main__":
    prepare()
