#!/usr/bin/env python3
"""Emit the least-privilege GCP IAM split for Teleo CI/CD and DB drills.

This script is intentionally dry-run by default. It does not read or print
secret values; it only emits the service accounts, roles, and commands needed
to separate Docker publishing from infra readiness and Cloud SQL restore drills.
"""

from __future__ import annotations

import argparse
import json
import shlex
from dataclasses import asdict, dataclass

PROJECT = "teleo-501523"
PROJECT_NUMBER = "785938879453"
GITHUB_REPOSITORY = "living-ip/teleo-infrastructure"
WIF_POOL = "github-actions"
WIF_PROVIDER = "living-ip-github"
BACKUP_BUCKET = "teleo-501523-prod-backups"
CLOUDSQL_INSTANCE = "teleo-pgvector-standby"

ARTIFACT_SERVICE_ACCOUNT = f"sa-artifact-builder@{PROJECT}.iam.gserviceaccount.com"
READINESS_SERVICE_ACCOUNT_ID = "sa-teleo-readiness"
READINESS_SERVICE_ACCOUNT = f"{READINESS_SERVICE_ACCOUNT_ID}@{PROJECT}.iam.gserviceaccount.com"
RESTORE_SERVICE_ACCOUNT_ID = "sa-teleo-restore-drill"
RESTORE_SERVICE_ACCOUNT = f"{RESTORE_SERVICE_ACCOUNT_ID}@{PROJECT}.iam.gserviceaccount.com"

READINESS_PROJECT_ROLES = [
    "roles/artifactregistry.reader",
    "roles/iam.serviceAccountViewer",
    "roles/iam.workloadIdentityPoolViewer",
    "roles/iam.securityReviewer",
    "roles/compute.viewer",
    "roles/cloudsql.viewer",
    "roles/storage.viewer",
    "roles/secretmanager.viewer",
]

RESTORE_PROJECT_ROLES = [
    "roles/cloudsql.editor",
]

RESTORE_BUCKET_ROLES = [
    "roles/storage.objectAdmin",
]


@dataclass(frozen=True)
class Binding:
    scope: str
    member: str
    role: str
    reason: str


def shell_join(parts: list[str]) -> str:
    return " ".join(shlex.quote(part) for part in parts)


def github_wif_member() -> str:
    return (
        f"principalSet://iam.googleapis.com/projects/{PROJECT_NUMBER}/locations/global/"
        f"workloadIdentityPools/{WIF_POOL}/attribute.repository/{GITHUB_REPOSITORY}"
    )


def build_plan() -> dict[str, object]:
    readiness_bindings = [
        Binding(
            scope=f"projects/{PROJECT}",
            member=f"serviceAccount:{READINESS_SERVICE_ACCOUNT}",
            role=role,
            reason="read-only GitHub readiness probe",
        )
        for role in READINESS_PROJECT_ROLES
    ]
    restore_bindings = [
        Binding(
            scope=f"projects/{PROJECT}",
            member=f"serviceAccount:{RESTORE_SERVICE_ACCOUNT}",
            role=role,
            reason="operator-triggered Cloud SQL import operation",
        )
        for role in RESTORE_PROJECT_ROLES
    ]
    restore_bucket_bindings = [
        Binding(
            scope=f"gs://{BACKUP_BUCKET}",
            member=f"serviceAccount:{RESTORE_SERVICE_ACCOUNT}",
            role=role,
            reason="upload and verify generated restore SQL in the backup bucket",
        )
        for role in RESTORE_BUCKET_ROLES
    ]

    commands = [
        shell_join(
            [
                "gcloud",
                "iam",
                "service-accounts",
                "create",
                READINESS_SERVICE_ACCOUNT_ID,
                "--project",
                PROJECT,
                "--display-name",
                "Teleo read-only infra readiness",
            ]
        ),
        shell_join(
            [
                "gcloud",
                "iam",
                "service-accounts",
                "add-iam-policy-binding",
                READINESS_SERVICE_ACCOUNT,
                "--project",
                PROJECT,
                "--member",
                github_wif_member(),
                "--role",
                "roles/iam.workloadIdentityUser",
            ]
        ),
        *[
            shell_join(
                [
                    "gcloud",
                    "projects",
                    "add-iam-policy-binding",
                    PROJECT,
                    "--member",
                    binding.member,
                    "--role",
                    binding.role,
                ]
            )
            for binding in readiness_bindings
        ],
        shell_join(
            [
                "gcloud",
                "iam",
                "service-accounts",
                "create",
                RESTORE_SERVICE_ACCOUNT_ID,
                "--project",
                PROJECT,
                "--display-name",
                "Teleo Cloud SQL restore drill",
            ]
        ),
        *[
            shell_join(
                [
                    "gcloud",
                    "projects",
                    "add-iam-policy-binding",
                    PROJECT,
                    "--member",
                    binding.member,
                    "--role",
                    binding.role,
                ]
            )
            for binding in restore_bindings
        ],
        *[
            shell_join(
                [
                    "gcloud",
                    "storage",
                    "buckets",
                    "add-iam-policy-binding",
                    binding.scope,
                    "--member",
                    binding.member,
                    "--role",
                    binding.role,
                ]
            )
            for binding in restore_bucket_bindings
        ],
        "CLOUDSQL_INSTANCE_SA=$(gcloud sql instances describe "
        + shlex.quote(CLOUDSQL_INSTANCE)
        + " --project "
        + shlex.quote(PROJECT)
        + " --format='value(serviceAccountEmailAddress)')",
        "gcloud storage buckets add-iam-policy-binding "
        + shlex.quote(f"gs://{BACKUP_BUCKET}")
        + ' --member "serviceAccount:${CLOUDSQL_INSTANCE_SA}" --role roles/storage.objectAdmin',
        shell_join(
            [
                "gh",
                "workflow",
                "run",
                "gcp-readiness.yml",
                "--repo",
                GITHUB_REPOSITORY,
                "--ref",
                "main",
                "-f",
                f"service_account={READINESS_SERVICE_ACCOUNT}",
            ]
        ),
    ]

    return {
        "artifact": "teleo_gcp_iam_split_plan",
        "project": PROJECT,
        "claim_ceiling": "dry_run_plan_only_until_applied_by_authenticated_gcp_admin",
        "kept_narrow": {
            "artifact_service_account": ARTIFACT_SERVICE_ACCOUNT,
            "reason": "Docker image publishing only; do not add broad infra-read or restore roles here.",
        },
        "readiness_service_account": READINESS_SERVICE_ACCOUNT,
        "restore_service_account": RESTORE_SERVICE_ACCOUNT,
        "github_wif_provider": f"projects/{PROJECT_NUMBER}/locations/global/workloadIdentityPools/{WIF_POOL}/providers/{WIF_PROVIDER}",
        "github_wif_member": github_wif_member(),
        "readiness_project_roles": READINESS_PROJECT_ROLES,
        "restore_project_roles": RESTORE_PROJECT_ROLES,
        "restore_bucket_roles": RESTORE_BUCKET_ROLES,
        "cloudsql_instance_service_account_binding": {
            "bucket": f"gs://{BACKUP_BUCKET}",
            "role": "roles/storage.objectAdmin",
            "reason": "Cloud SQL imports read the SQL object through the Cloud SQL instance service account.",
        },
        "bindings": {
            "readiness": [asdict(binding) for binding in readiness_bindings],
            "restore_operator": [asdict(binding) for binding in restore_bindings],
            "restore_bucket": [asdict(binding) for binding in restore_bucket_bindings],
        },
        "commands": commands,
        "post_apply_checks": [
            f"gh workflow run gcp-readiness.yml --repo {GITHUB_REPOSITORY} --ref main -f service_account={READINESS_SERVICE_ACCOUNT}",
            "EXECUTE=1 ops/run_gcp_cloudsql_restore_drill.sh",
            "retain target-counts.sql query readback before claiming GCP DB redundancy",
        ],
    }


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--format", choices=("json", "shell"), default="json")
    args = parser.parse_args()

    plan = build_plan()
    if args.format == "shell":
        print("# Dry-run command plan. Review before executing with an authenticated GCP admin.")
        for command in plan["commands"]:
            print(command)
    else:
        print(json.dumps(plan, indent=2, sort_keys=True))
    return 0


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