# @horizon-integrations/jetimob-crm

Adapter de integração com o **CRM Jetimob** para o ecossistema Horizon. Cumpre o
contrato canônico do `@horizon-js/integrations-core` — padrão Airbyte CDK,
TypeScript-first (Hexagonal Architecture + Anti-Corruption Layer).

Converte os imóveis da API Jetimob para o formato `HorizonProperty`
(`@horizon-js/property-domain-schema`).

## Instalação

```bash
pnpm add @horizon-integrations/jetimob-crm
```

## Credenciais

A autenticação da Jetimob é uma única **`webserviceKey`** — ela vai embutida no
path da URL (`/webservice/{webserviceKey}/imoveis`). Obtida no painel Jetimob da
imobiliária.

```ts
const credentials = { webserviceKey: "sua-chave-webservice" }
```

## Uso

### Camada 1 — Núcleo Airbyte (`Source` / `Stream`)

Forma canônica. Use quando precisar de streaming, sync incremental ou state.

```ts
import { JetimobSource } from "@horizon-integrations/jetimob-crm"
import { SyncMode } from "@horizon-js/integrations-core"

const source = new JetimobSource()

// valida a credencial (request barato no /imoveis-ativos)
const { ok, message } = await source.check(credentials)
if (!ok) throw new Error(message)

// lê os imóveis convertidos (paginação automática)
const [stream] = source.streams(credentials)
for await (const property of stream.readRecords(SyncMode.FULL_REFRESH)) {
  await db.properties.upsert(property)
}

// sync incremental — filtra server-side via ?start={unix}, traz só o delta
const state = { cursor: "2026-05-01T00:00:00.000Z" }
for await (const property of stream.readRecords(SyncMode.INCREMENTAL, state)) {
  await db.properties.upsert(property)
}
```

### Camada 2 — Funções de conveniência

DX de alto nível — mesmos nomes em todos os pacotes `@horizon-integrations/*`.

```ts
import { fetchAll, getListing, fetchByRef } from "@horizon-integrations/jetimob-crm"

// baixa + converte todos os imóveis em memória
const { properties, errors } = await fetchAll({ credentials })

// lista leve { ref, updatedAt }[] — usa /imoveis-ativos (~0.3s)
const listing = await getListing({ credentials })

// um imóvel pelo id_imovel (ou codigo)
const property = await fetchByRef({ credentials }, "6570922")
```

### Camada 3 — Conversão + schemas

```ts
import { convertJetimobPropertyToHorizon } from "@horizon-integrations/jetimob-crm"

const horizonProperty = convertJetimobPropertyToHorizon(rawJetimobImovel)
```

## A API Jetimob — o que importa saber

Tudo isto está testado contra a API real e declarado em `jetimobManifest`:

| Recurso | Endpoint / Param | Comportamento |
|---|---|---|
| Dump completo | `GET /imoveis?v=4&page=&pageSize=` | Envelope `{ total, page, pageSize, totalPages, data: [...] }`. ~96 campos por imóvel |
| **Lista leve** | `GET /imoveis-ativos?v=4` | Só os `id_imovel` ativos, numa tacada (~0.3s). É o que `getListing()` usa |
| **Incremental** | `?start={unix}` / `?end={unix}` | Filtra por data de atualização **server-side**. Unix timestamp (segundos) |
| Busca individual | `?id={id_imovel}` ou `?codigos={codigo}` | `id` ≠ `codigo` — `fetchByRef` aceita os dois |
| Seleção de campos | — | **Não existe.** `/imoveis` sempre devolve os ~96 campos |

`id_imovel` vs `codigo`: `reference` no formato Horizon é o **`codigo`**. O
`id_imovel` é exposto como campo extra (`id_imovel`) — é o que `/imoveis-ativos`
lista e o que `getListing().ref` devolve.

## Documentação

- [`docs/API_REFERENCE.md`](./docs/API_REFERENCE.md) — métodos públicos do pacote
- [`docs/FICHA_TECNICA_INTEGRACAO.md`](./docs/FICHA_TECNICA_INTEGRACAO.md) — mapeamento campo-a-campo Jetimob → Horizon
- [`docs/API_JETIMOB.md`](./docs/API_JETIMOB.md) — referência da API externa Jetimob
- [`CHANGELOG.md`](./CHANGELOG.md)
- [`examples/`](./examples/) — scripts de exemplo

## Build

```bash
pnpm install
pnpm typecheck
pnpm test
pnpm build
```
