#!/usr/bin/env python3
"""arq-gcp — mesh-routed GCP bridge citizen (v0 Phase A, template-based).

Per arq://doc/principle/arq-gcp-bridge-authority-bounds-v1 (DRAFT) and
arq://doc/principle/cryptographic-citizenship-hack-impossible-reconstruct-possible-v1
(parent, merged via #4014). First citizen using the shared CitizenBridge
template (#4018) — demonstrates that the cost of citizen N+1 collapses
to ~150 LOC once the template lands.

Phase A scope (this file): GCS `object list` + `object download` verbs.
Other GCP service surfaces (Secret Manager, WIF, SA, KMS, BigQuery) are
covered today by the out-of-tree `~/.local/bin/arq-gcp` primitive — Phase B
retrofit moves that into the repo + onto the same template + onto this
citizen's authority bounds. See the principle's "State as of 2026-05-22"
section for the three-step retrofit map.

Operator-tier prerequisites:
  - Create GCP SA scoped to roles/storage.objectViewer on the ARQERA project
  - Generate SA JSON key (download once, place at safe local path)
  - arq-connection put gcp.com arq-gcp-bridge-v1.service-account-json-path /path/to/sa.json
  - arq-connection put gcp.com arq-gcp-bridge-v1.default-project <project-id>
"""
from __future__ import annotations

import argparse
import os
import subprocess
import sys
from pathlib import Path

# Sibling-import the citizen-bridge template (lives next to this file).
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _citizen_bridge import CitizenBridge, utc_ts  # noqa: E402


BRIDGE = CitizenBridge(
    citizen_address="arq://body/peer/arq-gcp-bridge-v1",
    principle_ref="arq://doc/principle/arq-gcp-bridge-authority-bounds-v1",
    vendor_service="gcp.com",
    credential_slug="arq-gcp-bridge-v1",
)

REMEDIATION = (
    "Operator: create a GCP SA scoped to roles/storage.objectViewer on the "
    "ARQERA project per arq-gcp-bridge-authority-bounds-v1, then "
    "`arq-connection put gcp.com arq-gcp-bridge-v1.service-account-json-path "
    "/path/to/sa.json` and `arq-connection put gcp.com "
    "arq-gcp-bridge-v1.default-project <project-id>`."
)


def _run_gsutil(args: list[str], sa_json_path: str, project: str) -> tuple[int, str, str]:
    """Shell out to gsutil with the bridge's SA credentials.

    Phase A uses gsutil (already installed; same pattern as existing
    ~/.local/bin/arq-gcp). Phase B may swap to google-auth + direct HTTP
    for finer credential scoping per emission.
    """
    env = os.environ.copy()
    env["GOOGLE_APPLICATION_CREDENTIALS"] = sa_json_path
    env["CLOUDSDK_CORE_PROJECT"] = project
    try:
        r = subprocess.run(
            ["gsutil", *args], env=env, check=False, timeout=60,
            capture_output=True, text=True,
        )
        return r.returncode, r.stdout or "", r.stderr or ""
    except FileNotFoundError:
        return -1, "", "gsutil not on PATH"
    except subprocess.TimeoutExpired:
        return -1, "", "gsutil timeout (60s)"


def cmd_gcs_object_list(args: argparse.Namespace) -> int:
    creds = BRIDGE.require_credentials(
        verb="gcs-object-list",
        resources=("service-account-json-path", "default-project"),
        remediation_text=REMEDIATION,
    )
    if creds is None:
        return 3
    sa_path, project = creds
    target = f"gs://{args.bucket}"
    if args.prefix:
        target += f"/{args.prefix.lstrip('/')}"
    rc, stdout, stderr = _run_gsutil(["ls", "-l", target], sa_path, project)
    ref = f"arq-gcp-gcs-object-list-{args.bucket}-{utc_ts()}"
    line_count = len([ln for ln in stdout.splitlines() if ln.strip()])
    BRIDGE.emit_act(
        "gcs_object_listed", ref,
        {
            "verb": "gcs.object-list",
            "bucket": args.bucket,
            "prefix": args.prefix or None,
            "project": project,
            "rc": rc,
            "result_line_count": line_count,
            "stderr_preview": stderr[:200] if stderr else None,
        },
    )
    if rc == 0:
        sys.stdout.write(stdout)
        return 0
    print(f"arq-gcp gcs-object-list failed (rc={rc}): {stderr[:200]}", file=sys.stderr)
    return 1


def cmd_gcs_object_download(args: argparse.Namespace) -> int:
    creds = BRIDGE.require_credentials(
        verb="gcs-object-download",
        resources=("service-account-json-path", "default-project"),
        remediation_text=REMEDIATION,
    )
    if creds is None:
        return 3
    sa_path, project = creds
    src = f"gs://{args.bucket}/{args.object.lstrip('/')}"
    dest = args.destination
    rc, stdout, stderr = _run_gsutil(["cp", src, dest], sa_path, project)
    ref = f"arq-gcp-gcs-object-download-{args.bucket}-{utc_ts()}"
    BRIDGE.emit_act(
        "gcs_object_downloaded", ref,
        {
            "verb": "gcs.object-download",
            "bucket": args.bucket,
            "object": args.object,
            "destination": dest,
            "project": project,
            "rc": rc,
            "stderr_preview": stderr[:200] if stderr else None,
        },
    )
    if rc == 0:
        print(f"gcs-object-downloaded: gs://{args.bucket}/{args.object} → {dest}")
        return 0
    print(f"arq-gcp gcs-object-download failed (rc={rc}): {stderr[:200]}", file=sys.stderr)
    return 1


def build_parser() -> argparse.ArgumentParser:
    p = argparse.ArgumentParser(
        prog="arq-gcp",
        description=(
            "Mesh-routed GCP bridge citizen (v0 Phase A · GCS verbs only via "
            "this template-based script; secret/wif/sa verbs still served by "
            "~/.local/bin/arq-gcp pending Phase B retrofit). Every verb wraps "
            "a GCP call in a substrate envelope signed by the arq-gcp-bridge "
            f"citizen. Per arq://doc/principle/arq-gcp-bridge-authority-bounds-v1."
        ),
    )
    sub = p.add_subparsers(dest="verb_class", required=True)

    gcs = sub.add_parser("gcs", help="Google Cloud Storage verbs").add_subparsers(
        dest="gcs_object_class", required=True,
    )
    obj = gcs.add_parser("object").add_subparsers(dest="verb", required=True)

    ls = obj.add_parser("list", help="list objects in a bucket (gsutil ls -l)")
    ls.add_argument("--bucket", required=True, help="GCS bucket name (no gs:// prefix)")
    ls.add_argument("--prefix", default=None, help="object prefix to filter")
    ls.set_defaults(func=cmd_gcs_object_list)

    dl = obj.add_parser("download", help="download an object to a local path (gsutil cp)")
    dl.add_argument("--bucket", required=True, help="GCS bucket name")
    dl.add_argument("--object", required=True, help="object key inside the bucket")
    dl.add_argument("--destination", required=True, help="local file path to write")
    dl.set_defaults(func=cmd_gcs_object_download)

    return p


from pathlib import Path as _ScopesPath
import sys as _scopes_sys
_scopes_sys.path.insert(0, str(_ScopesPath(__file__).parent))
from _arq_provider_base import handle_meta_flags  # noqa: E402

PROVIDER = "gcp"
REQUIRED_SCOPES: dict[str, list[str]] = {
    # GCP OAuth 2.0 scope URLs (full URL is the canonical scope identifier).
    "gcs object list": ["https://www.googleapis.com/auth/devstorage.read_only"],
    "gcs object download": ["https://www.googleapis.com/auth/devstorage.read_only"],
}

def main() -> int:
    handle_meta_flags(PROVIDER, REQUIRED_SCOPES)
    args = build_parser().parse_args()
    return args.func(args)


if __name__ == "__main__":
    sys.exit(main())
