# XamiLib Python — Referencia de clases

Paquete: `xamisdk_client`. El punto de entrada es la clase `XamiSDK`. Los métodos siguen
la convención Python (snake_case). La interfaz es equivalente a la de PHP.

---

## `XamiSDK`

Punto de entrada de la librería.

### Constructor

```python
XamiSDK(config: dict)
```

`config` acepta:

| Clave | Descripción |
|---|---|
| `endpoint` | URL del servicio local (por defecto `http://127.0.0.1:8300`). |
| `wrapper_key` | Credencial de aplicación (`wrp_...`). |
| `wrapper_secret` | Secret de la credencial (`wsec_...`). |
| `timeout` | Timeout HTTP en segundos (opcional). |

### Métodos

| Método | Devuelve | Descripción |
|---|---|---|
| `pades()` | `Pades` | Servicio de firma de documentos PDF. |
| `blockchain()` | `Blockchain` | Servicio de firma de transacciones EVM. |
| `credentials()` | `Credentials` | Consulta de credenciales. |
| `designs()` | `Designs` | Consulta de diseños de sello. |
| `health()` | `Health` | Estado del servicio local. |
| `results()` | `Results` | Resultados de firma recientes. *(xamiSDK ≥ 0.2.0)* |
| `errors()` | `Errors` | Errores registrados por el servicio local. *(xamiSDK ≥ 0.2.0)* |
| `sync() -> dict` | resumen | Recarga credenciales y diseños desde Xami sin reiniciar. *(xamiSDK ≥ 0.2.0)* |

---

## `Pades`

Firma de documentos PDF (PAdES).

### Métodos

#### `sign(pdf_bytes: bytes, opts: dict) -> str`
Envía un PDF a firmar. Devuelve el `request_id`. `opts`: `credential_key` (requerido),
`design_key`, `variables`, `reason`, `location`, `signer_name`.

#### `result(request_id: str) -> dict`
Consulta el estado. Cuando está `DONE`, incluye el PDF firmado en la clave `pdf`.

#### `wait(request_id: str, timeout_seconds: int = 60, poll_seconds: float = 1.0) -> bytes`
Bloquea hasta que la firma esté lista y devuelve el PDF firmado (bytes).

#### `sign_and_wait(pdf_bytes: bytes, opts: dict, timeout_seconds: int = 60) -> bytes`
Atajo: `sign()` + `wait()`. Devuelve el PDF firmado.

### Ejemplo

```python
pdf_firmado = xami.pades().sign_and_wait(
    open("documento.pdf", "rb").read(),
    {
        "credential_key": "TU_CREDENCIAL",
        "signer_name": "Tu Nombre",
        "reason": "Aprobación",
        "variables": {"name": "Tu Nombre"},
    },
)
open("documento_firmado.pdf", "wb").write(pdf_firmado)
```

---

## `Blockchain`

Firma de transacciones / mensajes EVM. El chip actúa como wallet y devuelve `r`/`s`/`v`;
tú ensamblas la transacción con tu web3.

### Métodos

#### `sign(tx_hash: str, opts: dict) -> str`
Envía el hash a firmar. Devuelve el `request_id`. `opts`: `credential_key` (requerido,
credencial EVM), `chain_id`.

#### `result(request_id: str) -> dict`
Consulta el estado.

#### `wait(request_id: str, timeout_seconds: int = 60, poll_seconds: float = 1.0) -> dict`
Bloquea hasta tener la firma. Devuelve `{"r": ..., "s": ..., "v": ..., "signature": ...}`.

#### `sign_and_wait(tx_hash: str, opts: dict, timeout_seconds: int = 60) -> dict`
Atajo: `sign()` + `wait()`.

### Ejemplo

```python
firma = xami.blockchain().sign_and_wait("0xHASH_DE_LA_TX", {
    "credential_key": "TU_CREDENCIAL_EVM",
    "chain_id": 648541,
})
# firma["r"], firma["s"], firma["v"]
```

---

## `Credentials`

Consulta de credenciales en la caché local.

| Método | Devuelve | Descripción |
|---|---|---|
| `all() -> list` | lista | Todas las credenciales disponibles. |
| `get(key: str)` | credencial o `None` | Una credencial por su `credential_key`. |

---

## `Designs`

Consulta de diseños de sello en la caché local.

| Método | Devuelve | Descripción |
|---|---|---|
| `all() -> list` | lista | Todos los diseños disponibles. |
| `get(key: str)` | diseño o `None` | Un diseño por su `design_key`. |

---

## `Health`

| Método | Devuelve | Descripción |
|---|---|---|
| `self() -> dict` | estado | Estado del servicio local (pareado, tenant, versión, etc.). |
| `version()` | versión | Versión del xamiSDK local. `None` si es anterior a 0.2.0. |
| `api_version() -> int` | entero | Versión del contrato local. `1` = sin `sync()`, `results()` ni `errors()`. |

---

## `Results`

*Requiere xamiSDK ≥ 0.2.0.*

`Pades.result()` necesita que conserves el `request_id`. Si tu proceso se reinició o el
push no llegó, ese documento quedaba inalcanzable desde la librería. Con `Results` puedes
recuperar lo firmado sin depender de tu propio almacenamiento.

| Método | Devuelve | Descripción |
|---|---|---|
| `recent(limit=50) -> list` | lista | Últimos resultados (`PENDING` / `DONE` / `ERROR`). |
| `pending(limit=50) -> list` | lista | Solo los que siguen esperando la firma del chip. |

```python
for r in xami.results().pending():
    print(r["request_id"], "esperando desde", r["created_at"])
```

---

## `Errors`

*Requiere xamiSDK ≥ 0.2.0.*

Devuelve el mensaje corto y el `error_id` de cada fallo. **Nunca devuelve el traceback**:
el detalle técnico solo sale del equipo del cliente cuando alguien ejecuta
`xamisdk report`. Está pensado para que tu aplicación pueda decirle al usuario qué falló
y con qué identificador pedir soporte.

| Método | Devuelve | Descripción |
|---|---|---|
| `recent(limit=20) -> list` | lista | Últimos errores registrados. |
| `for_request(request_id) -> list` | lista | Errores de un request concreto. |

```python
fallos = xami.errors().for_request(request_id)
if fallos:
    print("No se pudo firmar:", fallos[0]["short"])
    print("Menciona", fallos[0]["error_id"], "al pedir soporte.")
```

---

## Compatibilidad de versiones

Los métodos marcados con *xamiSDK ≥ 0.2.0* devuelven `404` contra un servicio anterior.
Compruébalo antes si no controlas la versión instalada:

```python
if xami.health().api_version() >= 2:
    xami.sync()
```

---

## Manejo de errores

Todos los métodos lanzan `xamisdk_client.XamiException` ante un fallo (credencial
inválida, servicio caído, timeout). Envuélvelos en `try/except`.

```python
from xamisdk_client import XamiException

try:
    pdf = xami.pades().sign_and_wait(pdf_bytes, {"credential_key": "..."})
except XamiException as e:
    print("Error al firmar:", e)
```
