import gzip
import hashlib
import json
import os
from pathlib import Path
import re
import shutil
import subprocess
import sys

FILES = ["compose.yaml", "compose.tls.yaml", "compose.legacy.yaml", "compose.integrations.yaml", "compose.backup.yaml", "compose.backup.local.yaml", "compose.backup.legacy.yaml",
         "docker/stack.env.example", "docker/create-secrets.php", "docker/SecretFiles.php",
         "docker/mysql/healthcheck.sh", "docker/mysql/dump.sh", "docker/redis/entrypoint.sh", "docker/meilisearch/entrypoint.sh",
         "ops/backup.py", "ops/backup_core.py", "ops/transport.py", "ops/restore_drill.py", "ops/operations.env.example",
         "ops/systemd/post76-backup.service", "ops/systemd/post76-backup.timer", "ops/systemd/post76-repository-check.service", "ops/systemd/post76-repository-check.timer",
         "docker/nginx/tls.conf.template", "README.md"]
FILES += ["compose.monitoring.yaml", "compose.errors.yaml", "docker/monitoring.env.example", "docker/create-monitoring-secrets.php",
          "docker/monitoring/exporter.sh", "docker/monitoring/glitchtip.py", "docker/monitoring/prometheus.yml", "docker/monitoring/post76.rules.yml",
          "docker/monitoring/alertmanager.yml", "docker/monitoring/grafana-datasources.yml", "docker/monitoring/grafana-dashboards.yml", "docker/monitoring/post76-dashboard.json",
          "ops/metrics.py", "ops/metrics_core.py", "ops/metric_format.py", "ops/render_monitoring.py",
          "ops/systemd/post76-metrics.service", "ops/systemd/post76-metrics.timer"]
FILES += ["compose.telemetry.yaml"]


def package(root, revision):
    if not re.fullmatch(r"[0-9a-f]{40}", revision):
        raise ValueError("The release must use a full Git commit SHA")
    root = Path(root).resolve()
    for name in FILES:
        source = root / name
        if source.is_symlink() or not source.is_file():
            raise ValueError("Release source must be a regular file: " + name)
    destination = root / "storage/app/private/ci-check/release" / revision
    destination.mkdir(parents=True, exist_ok=False)
    environment = {"PATH": os.environ["PATH"], "DOCKER_HOST": "unix:///var/run/docker.sock",
                   "DOCKER_CONFIG": str(destination / "docker-config")}
    images = [f"post76-php:{revision}", f"post76-web:{revision}", f"post76-backup:{revision}"]
    for source, image in zip(["post76-php:ci", "post76-web:ci", "post76-backup:ci"], images):
        subprocess.run(["docker", "image", "tag", source, image], env=environment, check=True)
    archive = destination / "images.tar.gz"
    process = subprocess.Popen(["docker", "image", "save", *images], env=environment, stdout=subprocess.PIPE)
    with gzip.open(archive, "wb") as target:
        shutil.copyfileobj(process.stdout, target)
    if process.wait() != 0:
        raise RuntimeError("Unable to export release images")
    for name in FILES:
        source = root / name
        target = destination / name
        target.parent.mkdir(parents=True, exist_ok=True)
        shutil.copyfile(source, target)
    (destination / "release.json").write_text(json.dumps({"revision": revision, "images": images}, indent=2))
    checksums = []
    for path in sorted(destination.rglob("*")):
        if not path.is_file():
            continue
        with path.open("rb") as source:
            digest = hashlib.file_digest(source, "sha256").hexdigest()
        checksums.append(digest + "  " + str(path.relative_to(destination)))
    (destination / "SHA256SUMS").write_text("\n".join(checksums) + "\n")
    print("Release bundle prepared: " + str(destination.relative_to(root)))


if __name__ == "__main__":
    if len(sys.argv) != 2:
        raise SystemExit("Usage: python3 ci/release_bundle.py <full-commit-sha>")
    package(Path(__file__).resolve().parent.parent, sys.argv[1])
