"""Deterministic signing for the WordPress Bridge trust core."""

from __future__ import annotations

import base64
import hashlib
import json
import re
import zipfile
from pathlib import Path

from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey

ALLOWED_SUFFIXES = {".php", ".css", ".js", ".json", ".md", ".mo", ".po", ".pot", ".txt", ".xml", ".lock"}
ALLOWED_EXTENSIONLESS = {"LICENSE", "AUTHORS"}
ZIP_TIMESTAMP = (1980, 1, 1, 0, 0, 0)
MAX_FILES = 2000
MAX_BYTES = 20 * 1024 * 1024


def build_bridge_release(source: Path, output: Path, private_key_path: Path) -> dict:
    source = source.resolve()
    manifest_path = source / "bridge-release.json"
    installer = source / "Installer.php"
    if not manifest_path.is_file() or not installer.is_file():
        raise ValueError("Bridge manifest or Installer.php is missing")
    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    required = {"version", "channel", "minimum_wordpress_version", "minimum_php_version", "release_notes"}
    if required - manifest.keys():
        raise ValueError("Bridge release manifest is incomplete")
    header = installer.read_text(encoding="utf-8")[:1000]
    match = re.search(r"^\s*\*\s*Version:\s*(\S+)", header, re.MULTILINE)
    if not match or match.group(1) != manifest["version"]:
        raise ValueError("Installer version does not match bridge manifest")
    core_path = source / "includes" / "security" / "bridge-core.php"
    if not core_path.is_file():
        raise ValueError("Bridge security core is missing")
    core = core_path.read_text(encoding="utf-8")
    core_match = re.search(
        r"private\s+const\s+BRIDGE_VERSION\s*=\s*['\"]([^'\"]+)['\"]\s*;",
        core,
    )
    if not core_match or core_match.group(1) != manifest["version"]:
        raise ValueError("Runtime Core version does not match bridge manifest")
    files = []
    size = 0
    for path in sorted(source.rglob("*")):
        if path.is_symlink():
            raise ValueError("Bridge source cannot contain symbolic links")
        if not path.is_file():
            continue
        if any(part in {".git", "node_modules", "cache", "logs", "tmp"} for part in path.parts):
            continue
        if path.name not in ALLOWED_EXTENSIONLESS and path.suffix.lower() not in ALLOWED_SUFFIXES:
            continue
        files.append(path)
        size += path.stat().st_size
    if not files or len(files) > MAX_FILES or size > MAX_BYTES:
        raise ValueError("Bridge release size or file count exceeds limits")
    output.mkdir(parents=True, exist_ok=True)
    artifact = output / f"wp-flask-bridge-{manifest['version']}.zip"
    with zipfile.ZipFile(artifact, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
        for path in files:
            relative = Path("wp-flask-bridge") / path.relative_to(source)
            info = zipfile.ZipInfo(relative.as_posix(), ZIP_TIMESTAMP)
            info.compress_type = zipfile.ZIP_DEFLATED
            info.create_system = 3
            info.external_attr = (0o100644 & 0xFFFF) << 16
            archive.writestr(info, path.read_bytes(), compress_type=zipfile.ZIP_DEFLATED, compresslevel=9)
    content = artifact.read_bytes()
    private_key = serialization.load_pem_private_key(private_key_path.read_bytes(), password=None)
    if not isinstance(private_key, Ed25519PrivateKey):
        raise ValueError("Bridge signing key must be Ed25519")
    release = {
        **manifest,
        "artifact_name": artifact.name,
        "artifact_size": len(content),
        "sha256": hashlib.sha256(content).hexdigest(),
        "signature_algorithm": "Ed25519",
        "signature": base64.b64encode(private_key.sign(content)).decode("ascii"),
    }
    release_path = output / f"{artifact.name}.release.json"
    release_path.write_text(json.dumps(release, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    return {**release, "artifact_path": str(artifact), "release_path": str(release_path)}
