#!/usr/bin/env python3
from __future__ import annotations

import argparse
from pathlib import Path

import yaml


MAX_CONSUMERS = 3
MIN_RESERVED_CONNECTIONS = 40
REVIEWED_CELLS = ("apps-pg", "apps-pg-2")


def verify(paths: list[Path]) -> dict[str, int]:
    cells: dict[str, dict] = {}
    role_owner: dict[str, str] = {}
    prefixes: dict[str, str] = {}
    for path in paths:
        document = yaml.safe_load(path.read_text(encoding="utf-8"))
        if document.get("kind") != "Cluster":
            raise ValueError(f"{path}: expected a CNPG Cluster")
        name = document["metadata"]["name"]
        if name not in REVIEWED_CELLS:
            raise ValueError(f"{path}: {name!r} is not a reviewed apps-pg cell")
        cells[name] = document
    missing = set(REVIEWED_CELLS) - cells.keys()
    if missing:
        raise ValueError(f"missing reviewed overflow desired state: {sorted(missing)}")

    counts: dict[str, int] = {}
    for name, document in cells.items():
        spec = document["spec"]
        roles = spec.get("managed", {}).get("roles", [])
        if len(roles) > MAX_CONSUMERS:
            raise ValueError(
                f"{name}: {len(roles)} roles exceeds the {MAX_CONSUMERS}-consumer ceiling"
            )
        max_connections = int(spec["postgresql"]["parameters"]["max_connections"])
        allocated = 0
        for role in roles:
            role_name = role["name"]
            if role_name in role_owner:
                raise ValueError(
                    f"role {role_name!r} appears in both {role_owner[role_name]} and {name}"
                )
            role_owner[role_name] = name
            limit = role.get("connectionLimit")
            if not isinstance(limit, int) or isinstance(limit, bool) or limit < 1:
                raise ValueError(f"{name}/{role_name}: positive connectionLimit is required")
            allocated += limit
        if allocated + MIN_RESERVED_CONNECTIONS > max_connections:
            raise ValueError(
                f"{name}: {allocated} consumer connections leave fewer than "
                f"{MIN_RESERVED_CONNECTIONS} reserved connections"
            )
        resources = spec.get("resources", {})
        if not resources.get("requests") or not resources.get("limits"):
            raise ValueError(f"{name}: pod requests and limits are required")
        prefix = spec["backup"]["barmanObjectStore"]["destinationPath"]
        if prefix in prefixes:
            raise ValueError(f"{name}: backup prefix is shared with {prefixes[prefix]}")
        prefixes[prefix] = name
        counts[name] = len(roles)
    return counts


def main() -> int:
    parser = argparse.ArgumentParser(description="Verify apps-pg cell admission controls")
    parser.add_argument("manifests", nargs="+", type=Path)
    args = parser.parse_args()
    try:
        counts = verify(args.manifests)
    except (KeyError, TypeError, ValueError, yaml.YAMLError) as error:
        parser.error(str(error))
    print(
        "apps-pg capacity verified: "
        + ", ".join(f"{name}={counts[name]}/{MAX_CONSUMERS}" for name in REVIEWED_CELLS)
    )
    return 0


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