"""Crea o actualiza (idempotente por nombre) un Team Environment de Hoppscotch.

Define las variables de un workspace Hoppscotch self-hosted via GraphQL,
resolviendo secretos desde `pass`: cualquier variable cuyo `value` empiece por
``pass:`` se resuelve con pass_get_secret y se marca como `secret=True`.

Idempotencia por nombre: lista los environments de la team y, si ya existe uno
con el `name` dado, lo actualiza; si no, lo crea. Las mutations estan protegidas
por GqlAuthGuard: el JWT de sesion (de hoppscotch_login) viaja en la cookie
`access_token`.

Los valores secretos NUNCA se logean ni aparecen en el output: `resolved_secrets`
lista solo los KEYS resueltos desde pass, jamas sus valores.
"""

import json

import requests

from infra.pass_get_secret import pass_get_secret

_LIST_QUERY = "query($t:ID!){ team(teamID:$t){ teamEnvironments{ id name } } }"
_CREATE_MUTATION = (
    "mutation($n:String!,$t:ID!,$v:String!){"
    " createTeamEnvironment(name:$n,teamID:$t,variables:$v){ id name } }"
)
_UPDATE_MUTATION = (
    "mutation($id:ID!,$n:String!,$v:String!){"
    " updateTeamEnvironment(id:$id,name:$n,variables:$v){ id name } }"
)

_PASS_PREFIX = "pass:"


def hoppscotch_set_environment(
    team_id: str,
    name: str,
    variables: list[dict],
    *,
    access_token: str,
    backend_url: str = "http://localhost:3170",
) -> dict:
    """Crea o actualiza un Team Environment de Hoppscotch (idempotente por nombre).

    Args:
        team_id: ID de la team duena del environment.
        name: nombre del environment. La idempotencia es por este nombre dentro
            de la team: si ya existe uno con este name se actualiza, si no se crea.
        variables: lista de dicts ``{"key": str, "value": str, "secret": bool}``.
            Si un `value` empieza por ``pass:`` el resto se resuelve como ruta de
            pass con pass_get_secret y el secreto resuelto se usa como value real,
            forzando `secret=True` en esa variable. Campos `secret` ausentes se
            tratan como False.
        access_token: JWT de sesion (de hoppscotch_login). Viaja en la cookie
            `access_token`, NO en el header Authorization.
        backend_url: base del backend Hoppscotch sin barra final. El endpoint
            GraphQL es ``{backend_url}/graphql``.

    Returns:
        Dict. En exito: ``{"status": "ok", "id": str, "name": str,
        "action": "created"|"updated", "resolved_secrets": list[str]}`` donde
        `resolved_secrets` son SOLO los keys resueltos desde pass (nunca valores).
        En error: ``{"status": "error", "error": str}`` (resolucion pass fallida,
        GraphQL errors, HTTP no 200, o fallo de transporte). Si una variable
        `pass:` no se puede resolver, NO se crea/actualiza el environment.
    """
    resolved: list[dict] = []
    resolved_secrets: list[str] = []

    for var in variables:
        key = var.get("key")
        value = var.get("value", "")
        secret = bool(var.get("secret", False))

        if isinstance(value, str) and value.startswith(_PASS_PREFIX):
            pass_path = value[len(_PASS_PREFIX):]
            secret_res = pass_get_secret(pass_path)
            if secret_res.get("status") != "ok":
                # NO crear el env a medias: aborta con el key afectado.
                return {
                    "status": "error",
                    "error": (
                        f"pass resolution failed for key {key!r} "
                        f"(path {pass_path!r}): {secret_res.get('error')}"
                    ),
                }
            value = secret_res["value"]
            secret = True
            resolved_secrets.append(key)

        resolved.append({"key": key, "value": value, "secret": secret})

    variables_json = json.dumps(resolved)

    # 1) Localiza un environment existente con este nombre (idempotencia).
    try:
        list_resp = requests.post(
            f"{backend_url}/graphql",
            json={"query": _LIST_QUERY, "variables": {"t": team_id}},
            cookies={"access_token": access_token},
            timeout=30.0,
        )
    except requests.RequestException as exc:
        return {"status": "error", "error": f"transport error: {exc}"}

    list_data = _parse_json(list_resp)
    if list_data is None:
        return {
            "status": "error",
            "error": f"non-JSON list response (HTTP {list_resp.status_code})",
        }
    if list_data.get("errors"):
        return {"status": "error", "error": "graphql errors", "data": list_data}

    team = (list_data.get("data") or {}).get("team") or {}
    existing_id = None
    for env in team.get("teamEnvironments") or []:
        if env.get("name") == name:
            existing_id = env.get("id")
            break

    # 2) Update si existe, create si no.
    if existing_id:
        query = _UPDATE_MUTATION
        gql_vars = {"id": existing_id, "n": name, "v": variables_json}
        result_field = "updateTeamEnvironment"
        action = "updated"
    else:
        query = _CREATE_MUTATION
        gql_vars = {"n": name, "t": team_id, "v": variables_json}
        result_field = "createTeamEnvironment"
        action = "created"

    try:
        resp = requests.post(
            f"{backend_url}/graphql",
            json={"query": query, "variables": gql_vars},
            cookies={"access_token": access_token},
            timeout=30.0,
        )
    except requests.RequestException as exc:
        return {"status": "error", "error": f"transport error: {exc}"}

    data = _parse_json(resp)
    if data is None:
        return {
            "status": "error",
            "error": f"non-JSON response (HTTP {resp.status_code})",
        }
    if data.get("errors"):
        return {"status": "error", "error": "graphql errors", "data": data}

    env = (data.get("data") or {}).get(result_field)
    if not env or not env.get("id"):
        return {
            "status": "error",
            "error": f"{result_field} returned no id",
            "data": data,
        }

    return {
        "status": "ok",
        "id": env["id"],
        "name": env.get("name", name),
        "action": action,
        "resolved_secrets": resolved_secrets,
    }


def _parse_json(resp):
    """Devuelve el JSON de la respuesta o None si no es JSON valido."""
    try:
        return resp.json()
    except ValueError:
        return None
