# Property Customizer

Sistema de transformação de dados PropertyV3 baseado em regras configuráveis.

## 🎯 Propósito

O Property Customizer permite aplicar transformações complexas em dados PropertyV3 através de regras declarativas, sem necessidade de escrever código customizado para cada transformação.

## 🚀 Instalação

```bash
pnpm add @horizon-modules/property-model-v3
```

## 📦 Importação

```typescript
import { 
  PropertyCustomizer, 
  propertyV3CustomMapper,
  type MapRules,
  type PropertyV3 
} from '@horizon-modules/property-model-v3'
```

## 🔧 Uso Básico

### Usando a Classe

```typescript
const mapRules: MapRules = {
  attributesRules: [
    {
      key: "tipo",
      condition: { eq: "Casa" },
      rules: [
        { fn: "upsertAttr", key: "tipo", value: "Casa/Sobrado" },
        { fn: "upsertAttr", key: "subtipo", value: "Casa" },
      ]
    }
  ]
}

const customizer = new PropertyCustomizer(mapRules)
const result = customizer.transform(propertyV3Data)
```

### Usando a Função Helper

```typescript
const result = propertyV3CustomMapper(propertyV3Data, mapRules)
```

## 📋 Estrutura das Regras

### Reference Rules

Aplicam transformações baseadas na referência do imóvel:

```typescript
const mapRules: MapRules = {
  referenceRules: {
    'REF-123': [
      { fn: 'upsertAttr', key: 'destaque', value: true },
      { fn: 'addToArray', key: 'tags', value: 'premium' }
    ],
    'REF-456': [
      { fn: 'upsertAttr', key: 'promocao', value: true }
    ]
  }
}
```

### Attributes Rules

Aplicam transformações baseadas em condições dos atributos:

```typescript
const mapRules: MapRules = {
  attributesRules: [
    {
      key: 'tipo',
      condition: { eq: 'Casa de Condomínio' },
      rules: [
        { fn: 'upsertAttr', key: 'tipo', value: 'Casa/Sobrado' },
        { fn: 'upsertAttr', key: 'subtipo', value: 'Casa' },
        { fn: 'addToArray', key: 'tags', value: 'em-condominio' }
      ]
    },
    {
      key: 'dormitorios',
      condition: { gte: 3 },
      rules: [
        { fn: 'addToArray', key: 'tags', value: 'muitos-quartos' }
      ]
    }
  ]
}
```

## 🎮 Actions Disponíveis

### upsertAttr
Cria ou atualiza um atributo:

```typescript
{ fn: 'upsertAttr', key: 'piscina', value: true }
```

### removeAttr
Remove um atributo:

```typescript
{ fn: 'removeAttr', key: 'campo_antigo' }
```

### addToArray
Adiciona valor(es) a um array:

```typescript
// Adicionar um valor
{ fn: 'addToArray', key: 'tags', value: 'nova-tag' }

// Adicionar múltiplos valores
{ fn: 'addToArray', key: 'caracteristicas', value: ['item1', 'item2'] }
```

### removeFromArray
Remove um valor de um array:

```typescript
{ fn: 'removeFromArray', key: 'caracteristicas', value: 'Piscina' }
```

## 🎯 Condições Suportadas

### Igualdade
- `eq` - igual a
- `not_eq` - diferente de

### Arrays
- `has` - contém todos os valores
- `has_any` - contém pelo menos um valor
- `not_has` - não contém valor(es)

### Numéricos
- `gt` - maior que
- `gte` - maior ou igual
- `lt` - menor que
- `lte` - menor ou igual

### Texto
- `contains` - contém substring (case insensitive)
- `starts_with` - começa com (case insensitive)
- `ends_with` - termina com (case insensitive)

### Booleanos
- `is_true` - é verdadeiro (true, "true", 1)
- `is_false` - é falso (false, "false", 0)

### Existência
- `exists` - campo existe e não está vazio
- `not_exists` - campo não existe ou está vazio

## 📝 Placeholders

Use `{{value}}` para referenciar o valor do atributo atual:

```typescript
{
  key: 'area',
  condition: { exists: true },
  rules: [
    { fn: 'upsertAttr', key: 'area_formatada', value: '{{value}} m²' }
  ]
}
```

## 🏷️ Tags de Description

O Property Customizer processa automaticamente tags especiais na description:

### Tags Simples

```typescript
const property: PropertyV3 = {
  description: 'Casa linda [[description-en]]Beautiful house[[/description-en]] com vista'
}

// Após processamento:
// property.attributes.description_en = 'Beautiful house'
// property.description = 'Casa linda com vista'
```

### Custom Attributes (JSON)

```typescript
const property: PropertyV3 = {
  description: `Imóvel especial
[[custom-attributes]]
{
  "energia_solar": true,
  "classificacao": "A+"
}
[[/custom-attributes]]
Com tecnologia sustentável`
}

// Após processamento:
// property.attributes.energia_solar = true
// property.attributes.classificacao = 'A+'
// property.description = 'Imóvel especial\nCom tecnologia sustentável'
```

## 💡 Exemplos Práticos

### Transformar Características em Campos Booleanos

```typescript
const mapRules: MapRules = {
  attributesRules: [
    {
      key: 'caracteristicas',
      condition: { has_any: ['Piscina'] },
      rules: [
        { fn: 'removeFromArray', key: 'caracteristicas', value: 'Piscina' },
        { fn: 'upsertAttr', key: 'piscina', value: true }
      ]
    },
    {
      key: 'caracteristicas',
      condition: { has_any: ['Churrasqueira'] },
      rules: [
        { fn: 'removeFromArray', key: 'caracteristicas', value: 'Churrasqueira' },
        { fn: 'upsertAttr', key: 'churrasqueira', value: true }
      ]
    }
  ]
}
```

### Normalizar Tipos de Imóveis

```typescript
const mapRules: MapRules = {
  attributesRules: [
    {
      key: 'tipo',
      condition: { eq: 'Casa de Condomínio' },
      rules: [
        { fn: 'upsertAttr', key: 'tipo', value: 'Casa/Sobrado' },
        { fn: 'upsertAttr', key: 'subtipo', value: 'Casa' },
        { fn: 'addToArray', key: 'tags', value: 'em-condominio' }
      ]
    },
    {
      key: 'tipo',
      condition: { eq: 'Apartamento Duplex' },
      rules: [
        { fn: 'upsertAttr', key: 'tipo', value: 'Apartamento' },
        { fn: 'upsertAttr', key: 'subtipo', value: 'Duplex' }
      ]
    }
  ]
}
```

### Adicionar Tags Baseadas em Corretor

```typescript
const mapRules: MapRules = {
  attributesRules: [
    {
      key: 'tags',
      condition: { has_any: ['agente-elvis'] },
      rules: [
        { fn: 'upsertAttr', key: 'corretor_id', value: 'elvis-ghisi' },
        { fn: 'upsertAttr', key: 'corretor_nome', value: 'Elvis Ghisi' },
        { fn: 'addToArray', key: 'tags', value: 'corretor-premium' }
      ]
    }
  ]
}
```

## 🧪 Testando

```typescript
import { describe, it, expect } from 'vitest'
import { propertyV3CustomMapper } from '@horizon-modules/property-model-v3'

describe('Minhas regras customizadas', () => {
  it('deve transformar corretamente', () => {
    const property = {
      attributes: {
        tipo: 'Casa de Condomínio'
      }
    }
    
    const result = propertyV3CustomMapper(property, mapRules)
    
    expect(result.attributes.tipo).toBe('Casa/Sobrado')
    expect(result.attributes.subtipo).toBe('Casa')
  })
})
```

## 📌 Notas Importantes

1. O Property Customizer **não modifica** o objeto original, sempre retorna uma cópia
2. As regras são aplicadas na ordem: description tags → reference rules → attribute rules
3. Para arrays, o `addToArray` automaticamente evita duplicatas usando `union`
4. Condições de texto são case-insensitive
5. Placeholders são resolvidos dinamicamente durante a execução

## 🔄 Migração de Código Legado

Se você tem código customizado para transformações, pode migrar facilmente:

**Antes:**
```typescript
if (property.attributes.tipo === 'Casa de Condomínio') {
  property.attributes.tipo = 'Casa/Sobrado'
  property.attributes.subtipo = 'Casa'
  property.attributes.tags = [...(property.attributes.tags || []), 'em-condominio']
}
```

**Depois:**
```typescript
const mapRules: MapRules = {
  attributesRules: [{
    key: 'tipo',
    condition: { eq: 'Casa de Condomínio' },
    rules: [
      { fn: 'upsertAttr', key: 'tipo', value: 'Casa/Sobrado' },
      { fn: 'upsertAttr', key: 'subtipo', value: 'Casa' },
      { fn: 'addToArray', key: 'tags', value: 'em-condominio' }
    ]
  }]
}

const result = propertyV3CustomMapper(property, mapRules)
```