#!/usr/bin/env python3
"""arq-oci — Oracle Cloud Infrastructure adapter for ARQERA mesh.

OCI CLI is the canonical client; this wraps via subprocess with
substrate-attestation per call.

v0 verbs:
  arq-oci tenancy info                    — root tenancy summary
  arq-oci region list                     — subscribed regions
  arq-oci compartment list                — compartments under root
  arq-oci instance list [--region R]      — compute instances (one region)
  arq-oci shape list [--region R]         — available compute shapes
  arq-oci usage [--days N]                — billing usage proxy (instance count + shape)
"""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent))
from _arq_provider_base import call_with_audit, print_json, handle_meta_flags

PROVIDER = "oci"


REQUIRED_SCOPES: dict[str, list[str]] = {
    # OCI IAM policy verbs (require matching policy statements on the user/principal).
    "tenancy info": ["inspect tenancies"],
    "region list": ["inspect tenancies"],
    "compartment list": ["read compartments"],
    "instance list": ["read instance-family"],
    "shape list": ["read instance-family"],
    "usage": ["read usage-report"],
}
def _tenancy_ocid() -> str | None:
    cfg = Path.home() / ".oci" / "config"
    if not cfg.exists():
        return None
    for line in cfg.read_text().splitlines():
        if line.startswith("tenancy="):
            return line.split("=", 1)[1].strip()
    return None


def _oci(args: list[str], region: str | None = None) -> tuple[int, str, str]:
    cmd = ["oci"] + args + ["--output", "json"]
    if region:
        cmd += ["--region", region]
    try:
        r = subprocess.run(cmd, capture_output=True, text=True, timeout=60, check=False)
        return r.returncode, r.stdout, r.stderr
    except FileNotFoundError:
        return 127, "", "oci CLI not on PATH"
    except subprocess.TimeoutExpired:
        return 124, "", "oci CLI timeout"


def _print_or_fail(code: int, stdout: str, stderr: str) -> int:
    if code != 0:
        sys.stderr.write(f"oci CLI exit {code}: {stderr[:200]}\n")
        return 1
    try:
        return print_json(json.loads(stdout))
    except json.JSONDecodeError:
        sys.stderr.write(f"non-JSON stdout: {stdout[:200]}\n")
        return 1


def _cmd_tenancy_info(args) -> int:
    t = _tenancy_ocid()
    if not t:
        sys.stderr.write("no tenancy OCID in ~/.oci/config\n")
        return 1
    return _print_or_fail(*_oci(["iam", "tenancy", "get", "--tenancy-id", t]))


def _cmd_region_list(args) -> int:
    t = _tenancy_ocid()
    if not t:
        sys.stderr.write("no tenancy OCID\n")
        return 1
    return _print_or_fail(*_oci(["iam", "region-subscription", "list", "--tenancy-id", t]))


def _cmd_compartment_list(args) -> int:
    t = _tenancy_ocid()
    if not t:
        sys.stderr.write("no tenancy OCID\n")
        return 1
    return _print_or_fail(*_oci(["iam", "compartment", "list", "--compartment-id", t, "--include-root", "--all"]))


def _cmd_instance_list(args) -> int:
    t = _tenancy_ocid()
    if not t:
        sys.stderr.write("no tenancy OCID\n")
        return 1
    return _print_or_fail(*_oci(["compute", "instance", "list", "--compartment-id", t, "--all"], region=args.region))


def _cmd_shape_list(args) -> int:
    t = _tenancy_ocid()
    if not t:
        sys.stderr.write("no tenancy OCID\n")
        return 1
    return _print_or_fail(*_oci(["compute", "shape", "list", "--compartment-id", t], region=args.region))


def _cmd_usage(args) -> int:
    """Best-effort usage proxy: count instances across subscribed regions + summarise shapes."""
    t = _tenancy_ocid()
    if not t:
        sys.stderr.write("no tenancy OCID\n")
        return 1
    code, stdout, stderr = _oci(["iam", "region-subscription", "list", "--tenancy-id", t])
    if code != 0:
        sys.stderr.write(f"region-subscription list failed: {stderr[:200]}\n")
        return 1
    try:
        regions = [r["region-name"] for r in json.loads(stdout)["data"]]
    except Exception as e:
        sys.stderr.write(f"region parse: {e}\n")
        return 1

    summary = {"tenancy": t[:40] + "...", "subscribed_regions": regions, "per_region": {}}
    for region in regions:
        c, so, _se = _oci(["compute", "instance", "list", "--compartment-id", t, "--all"], region=region)
        try:
            insts = json.loads(so).get("data", []) if c == 0 and so else []
        except Exception:
            insts = []
        shapes = {}
        for i in insts:
            s = i.get("shape", "unknown")
            shapes[s] = shapes.get(s, 0) + 1
        summary["per_region"][region] = {"instance_count": len(insts), "shape_breakdown": shapes}
    return print_json(summary)


def main() -> int:
    handle_meta_flags(PROVIDER, REQUIRED_SCOPES)
    p = argparse.ArgumentParser(prog="arq-oci")
    sub = p.add_subparsers(dest="cmd", required=True)

    s_t = sub.add_parser("tenancy")
    s_t_s = s_t.add_subparsers(dest="action", required=True)
    s_t_i = s_t_s.add_parser("info")
    s_t_i.set_defaults(func=_cmd_tenancy_info, verb="tenancy info")

    s_r = sub.add_parser("region")
    s_r_s = s_r.add_subparsers(dest="action", required=True)
    s_r_l = s_r_s.add_parser("list")
    s_r_l.set_defaults(func=_cmd_region_list, verb="region list")

    s_c = sub.add_parser("compartment")
    s_c_s = s_c.add_subparsers(dest="action", required=True)
    s_c_l = s_c_s.add_parser("list")
    s_c_l.set_defaults(func=_cmd_compartment_list, verb="compartment list")

    s_i = sub.add_parser("instance")
    s_i_s = s_i.add_subparsers(dest="action", required=True)
    s_i_l = s_i_s.add_parser("list")
    s_i_l.add_argument("--region", default=None)
    s_i_l.set_defaults(func=_cmd_instance_list, verb="instance list")

    s_s = sub.add_parser("shape")
    s_s_s = s_s.add_subparsers(dest="action", required=True)
    s_s_l = s_s_s.add_parser("list")
    s_s_l.add_argument("--region", default=None)
    s_s_l.set_defaults(func=_cmd_shape_list, verb="shape list")

    s_u = sub.add_parser("usage")
    s_u.add_argument("--days", type=int, default=30)
    s_u.set_defaults(func=_cmd_usage, verb="usage")

    args = p.parse_args()
    return call_with_audit(PROVIDER, args.verb, args.func, args)


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