#!/usr/bin/env python3
"""Validate the Teleo GCP service-to-service communication contract."""

from __future__ import annotations

import argparse
import ipaddress
import json
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

BROAD_CIDRS = {"0.0.0.0/0", "::/0"}
SECRET_FIELD_TOKENS = ("password", "private_key", "secret_value", "token_value", "seed_phrase")
DEFAULT_COMPUTE_SUFFIX = "-compute@developer.gserviceaccount.com"
REQUIRED_PATHS = {
    "github_actions_to_artifact_registry",
    "github_actions_to_readiness_probe",
    "operator_ssh_to_teleo_vms",
    "teleo_vms_to_artifact_registry",
    "teleo_vms_to_cloudsql_private",
    "cloudsql_import_from_backup_bucket",
    "restore_drill_operator_to_cloudsql_admin",
    "vps_backup_to_gcs_bucket",
}


def load_contract(path: Path) -> dict[str, Any]:
    return json.loads(path.read_text())


def as_list(value: Any) -> list[Any]:
    return value if isinstance(value, list) else []


def nested_values(value: Any) -> list[Any]:
    if isinstance(value, dict):
        result: list[Any] = []
        for item in value.values():
            result.extend(nested_values(item))
        return result
    if isinstance(value, list):
        result = []
        for item in value:
            result.extend(nested_values(item))
        return result
    return [value]


def find_secret_fields(value: Any, prefix: str = "") -> list[str]:
    problems: list[str] = []
    if isinstance(value, dict):
        for key, item in value.items():
            path = f"{prefix}.{key}" if prefix else str(key)
            if str(key).lower() in SECRET_FIELD_TOKENS:
                problems.append(path)
            problems.extend(find_secret_fields(item, path))
    elif isinstance(value, list):
        for index, item in enumerate(value):
            problems.extend(find_secret_fields(item, f"{prefix}[{index}]"))
    return problems


def validate_cidrs(path: dict[str, Any]) -> list[str]:
    problems: list[str] = []
    cidrs = as_list(path.get("source", {}).get("cidrs"))
    for cidr in cidrs:
        if cidr in BROAD_CIDRS:
            problems.append(f"{path['name']}:broad_source_cidr={cidr}")
            continue
        if cidr == "<operator-ip>/32":
            continue
        try:
            network = ipaddress.ip_network(str(cidr), strict=False)
        except ValueError:
            problems.append(f"{path['name']}:invalid_source_cidr={cidr}")
            continue
        if path.get("ports") == [22] and (network.version != 4 or network.prefixlen != 32):
            problems.append(f"{path['name']}:ssh_source_not_single_ipv4_32={cidr}")
    return problems


def validate_path(path: dict[str, Any]) -> list[str]:
    problems: list[str] = []
    name = path.get("name", "<unnamed>")
    for field in ("name", "purpose", "source", "identity", "target", "protocol", "ports", "network_path", "encryption", "allowed_by"):
        if field not in path:
            problems.append(f"{name}:missing_field={field}")

    ports = as_list(path.get("ports"))
    if any(not isinstance(port, int) for port in ports):
        problems.append(f"{name}:ports_must_be_integers")
    if 22 in ports and name != "operator_ssh_to_teleo_vms":
        problems.append(f"{name}:tcp_22_only_allowed_for_operator_ssh")
    if 3389 in ports:
        problems.append(f"{name}:rdp_forbidden")

    problems.extend(validate_cidrs(path))

    values = [str(value) for value in nested_values(path)]
    if any(value in BROAD_CIDRS for value in values):
        problems.append(f"{name}:contains_broad_cidr")
    if any(value.endswith(DEFAULT_COMPUTE_SUFFIX) for value in values):
        problems.append(f"{name}:default_compute_service_account_forbidden")

    target = path.get("target", {})
    if target.get("type") == "cloudsql_postgres":
        if target.get("public_ip") is not False:
            problems.append(f"{name}:cloudsql_public_ip_not_false")
        if not target.get("private_network"):
            problems.append(f"{name}:cloudsql_missing_private_network")
        if path.get("encryption") != "encrypted_only":
            problems.append(f"{name}:cloudsql_encryption_not_encrypted_only")

    if path.get("protocol") in {"https", "postgres"} and path.get("encryption") in {None, "none", "disabled"}:
        problems.append(f"{name}:encrypted_protocol_without_encryption")
    return problems


def build_payload(contract_path: Path) -> dict[str, Any]:
    contract = load_contract(contract_path)
    paths = as_list(contract.get("allowed_paths"))
    names = {str(path.get("name")) for path in paths if isinstance(path, dict)}
    problems: list[str] = []

    missing_paths = sorted(REQUIRED_PATHS - names)
    if missing_paths:
        problems.append(f"missing_required_paths={missing_paths}")
    extra_secret_fields = find_secret_fields(contract)
    if extra_secret_fields:
        problems.append(f"secret_value_fields_present={extra_secret_fields}")

    invariants = contract.get("global_invariants", {})
    for invariant in (
        "no_public_database_ip",
        "no_broad_ssh_or_rdp",
        "no_default_compute_service_accounts",
        "secret_values_not_stored_in_contract",
        "database_connections_encrypted_only",
    ):
        if invariants.get(invariant) is not True:
            problems.append(f"invariant_not_true={invariant}")

    for path in paths:
        if not isinstance(path, dict):
            problems.append("allowed_paths_contains_non_object")
            continue
        problems.extend(validate_path(path))

    return {
        "artifact": "teleo_gcp_service_communications_check",
        "generated_at_utc": datetime.now(UTC).isoformat(),
        "contract_path": str(contract_path),
        "project": contract.get("project"),
        "network": contract.get("network"),
        "allowed_path_count": len(paths),
        "required_path_count": len(REQUIRED_PATHS),
        "status": "pass" if not problems else "fail",
        "problems": problems,
        "not_proven_by_this_artifact": contract.get("not_proven_by_this_contract", []),
    }


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--contract", default="config/gcp-service-communications.json", type=Path)
    parser.add_argument(
        "--output",
        default="outputs/gcp-infra-hardening-20260707/proofs/gcp-service-communications-check.json",
        type=Path,
    )
    args = parser.parse_args()
    payload = build_payload(args.contract)
    args.output.parent.mkdir(parents=True, exist_ok=True)
    args.output.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n")
    print(json.dumps(payload, indent=2, sort_keys=True))
    return 0 if payload["status"] == "pass" else 1


if __name__ == "__main__":
    raise SystemExit(main())
