
# Mesclagem no nível da célula

A mesclagem no nível das colunas detecta quando os dois lados alteraram a mesma coluna. A mesclagem no nível da célula vai além e mescla o conteúdo dentro dessa coluna. Duas pessoas podem editar linhas diferentes de um campo TEXT ou chaves diferentes de um objeto JSON sem gerar conflitos.

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;

$config = CellMergeConfig::auto();
$merge = new ThreeWayMerge($config);
$result = $merge->merge($base, $ours, $theirs);
```

## Ativação da mesclagem no nível da célula

A mesclagem no nível da célula é opcional. Ela só é executada em um `ThreeWayMerge` construído com um `CellMergeConfig`. Sem esse elemento, toda coluna que os dois lados alterarem para valores diferentes será um conflito `update_update`.

A fachada `Merql` não recebe uma configuração: `Merql::merge()`, `Merql::patch()` e `Merql::plan()` constroem `new ThreeWayMerge()` sem uma, e o comando `merge` de CLI passa por `Merql::merge()`. Eles sempre fazem a mesclagem no nível das colunas ([#7](https://github.com/inline0/merql/issues/7)).

Para mesclar snapshots salvos pela fachada ou pelo CLI com mesclagem no nível da célula, carregue-os e chame `ThreeWayMerge` diretamente. O resultado é um `MergeResult` comum, que a fachada ainda pode aplicar, e `MergePlanBuilder` o transforma em um [plano de mesclagem](https://merql.inline0.dev/pt/docs/merge/plans):

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\Merge\ThreeWayMerge;
use Merql\Merql;
use Merql\Plan\MergePlanBuilder;
use Merql\Snapshot\SnapshotStore;

$base   = SnapshotStore::load('base');
$ours   = SnapshotStore::load('ours');
$theirs = SnapshotStore::load('theirs');

$result = (new ThreeWayMerge(CellMergeConfig::auto()))->merge($base, $ours, $theirs);

$plan = (new MergePlanBuilder())->build('plan-1', $base, $ours, $theirs, $result);
```

Os mescladores de células só são executados para colunas alteradas pelos dois lados. Se apenas um lado alterou uma coluna, seu valor é mantido como está.

## CellMergeConfig

`CellMergeConfig` mapeia colunas ou tipos de coluna para implementações de mescladores de células. A fábrica `auto()` fornece valores padrão sensatos:

```php
use Merql\CellMerge\CellMergeConfig;
use Merql\CellMerge\JsonCellMerger;
use Merql\CellMerge\TextCellMerger;

// Auto: column types containing "text" (TEXT, MEDIUMTEXT, LONGTEXT, ...) use
// TextCellMerger, column types containing "json" use JsonCellMerger.
$config = CellMergeConfig::auto();

// Custom: assign mergers to specific columns.
$config = (new CellMergeConfig())
    ->forColumn('posts.content', new TextCellMerger())
    ->forColumn('settings', new JsonCellMerger())
    ->forType('json', new JsonCellMerger());
```

Prioridade de resolução do mesclador:

1. Nome qualificado da coluna (`table.column`).
2. Nome não qualificado da coluna (`column`).
3. Correspondência de padrão do tipo de coluna (`str_contains` sem distinção entre maiúsculas e minúsculas).
4. Padrão: `OpaqueCellMerger` (sempre informa um conflito quando os valores diferem).

## TextCellMerger

Mescla textos com várias linhas usando o diff de Myers, o mesmo algoritmo usado por git para o conteúdo de arquivos. Cada linha é tratada como uma unidade, e as alterações em linhas diferentes são mescladas sem conflitos.

```php
use Merql\CellMerge\TextCellMerger;

$merger = new TextCellMerger();
```

### Exemplo: mesclagem de texto sem conflitos

```
Base:    "line one\nline two\nline three"
Ours:    "LINE ONE\nline two\nline three"    // changed line 1
Theirs:  "line one\nline two\nLINE THREE"    // changed line 3
```

Foram editadas linhas diferentes. A mesclagem gera `"LINE ONE\nline two\nLINE THREE"` sem conflitos.

### Exemplo: conflito de texto

```
Base:    "line one\nline two"
Ours:    "changed by us\nline two"     // changed line 1
Theirs:  "changed by them\nline two"   // also changed line 1
```

Os dois lados alteraram a mesma linha para valores diferentes. O mesclador retorna um resultado de conflito. O conteúdo mesclado inclui marcadores de conflito semelhantes aos de git.

### Como funciona

`TextCellMerger` delega para o `ThreeWayMerge::merge()` do pitmaster, que calcula um diff de Myers entre a base e cada lado e, em seguida, combina os dois scripts de edição. Quando as edições se sobrepõem na mesma região de linhas, ele informa um conflito.

## JsonCellMerger

Mescla objetos JSON comparando cada chave de nível superior de forma independente. Cada chave é mesclada segundo as regras padrão de três vias.

```php
use Merql\CellMerge\JsonCellMerger;

$merger = new JsonCellMerger();
```

### Exemplo: mesclagem de JSON sem conflitos

```json
// Base
{"name": "Alice", "role": "editor", "active": true}

// Ours: changed "role"
{"name": "Alice", "role": "admin", "active": true}

// Theirs: changed "name"
{"name": "Bob", "role": "editor", "active": true}
```

Foram alteradas chaves diferentes. O resultado mesclado é:

```json
{"name": "Bob", "role": "admin", "active": true}
```

### Exemplo: conflito de JSON

```json
// Base
{"color": "red"}

// Ours
{"color": "blue"}

// Theirs
{"color": "green"}
```

Os dois lados alteraram a chave `color` para valores diferentes. Isso é um conflito. Por padrão, o mesclador escolhe nosso e informa um conflito.

### Regras no nível da chave

| Base | Nosso | Deles | Resultado |
|---|---|---|---|
| Chave inalterada | Chave inalterada | Aceitar base |
| Chave inalterada | Chave alterada | Aceitar deles |
| Chave alterada | Chave inalterada | Aceitar nosso |
| Chave alterada (igual) | Chave alterada (igual) | Aceitar (acordo) |
| Chave alterada (diferente) | Chave alterada (diferente) | **Conflito** (padrão: nosso) |
| Chave existente | Chave existente | Chave removida | Aceitar remoção |
| Chave existente | Chave removida | Chave existente | Aceitar remoção |
| Chave ausente | Chave adicionada | Chave ausente | Aceitar adição |
| Chave ausente | Chave ausente | Chave adicionada | Aceitar adição |

Objetos aninhados são comparados como strings JSON opacas, sem comparação recursiva. Se um objeto aninhado for diferente, compara-se o valor inteiro daquela chave, e não suas subchaves.

### Requisitos

Os três valores precisam ser objetos ou arrays JSON válidos. Se algum valor não for JSON válido ou for um escalar JSON (string, número, booleano), o mesclador recorrerá a um conflito.

## OpaqueCellMerger

O mesclador padrão quando nenhuma estratégia no nível da célula está configurada. Ele trata o valor da coluna como um bloco opaco e sempre informa um conflito quando os dois lados diferem.

```php
use Merql\CellMerge\OpaqueCellMerger;

$merger = new OpaqueCellMerger();
$result = $merger->merge($base, $ours, $theirs);
// $result->clean is always false when $ours !== $theirs
```

## CellMergeResult

Todos os mescladores de células retornam um `CellMergeResult`:

```php
$result = $merger->merge($base, $ours, $theirs);

$result->clean;     // bool: true if merge resolved cleanly
$result->value;     // mixed: the merged value (or ours on conflict)
$result->conflicts; // int: number of conflicts within the cell
```

Métodos de fábrica:

```php
CellMergeResult::resolved($value);           // clean merge
CellMergeResult::conflict($oursValue, $n);   // n conflicts, defaults to ours
```

## Mescladores de células personalizados

Implemente a interface `CellMerger` para adicionar sua própria estratégia de mesclagem:

```php
use Merql\CellMerge\CellMerger;
use Merql\CellMerge\CellMergeResult;

class YamlCellMerger implements CellMerger
{
    public function merge(mixed $base, mixed $ours, mixed $theirs): CellMergeResult
    {
        // Parse YAML, merge keys, return result.
        $merged = $this->mergeYaml($base, $ours, $theirs);

        if ($merged['clean']) {
            return CellMergeResult::resolved($merged['value']);
        }

        return CellMergeResult::conflict($ours);
    }
}

$config = (new CellMergeConfig())
    ->forColumn('config_data', new YamlCellMerger());
```
