"""Tests for sync engine data models."""
import pytest

from greymatter_core.sync.models import (
    SyncOperation,
    SyncLogEntry,
    SyncState,
    SyncBatch,
    SyncResult,
)


class TestSyncOperation:
    def test_upsert_value(self):
        assert SyncOperation.UPSERT.value == "upsert"

    def test_delete_value(self):
        assert SyncOperation.DELETE.value == "delete"

    def test_from_string(self):
        assert SyncOperation("upsert") == SyncOperation.UPSERT
        assert SyncOperation("delete") == SyncOperation.DELETE

    def test_invalid_raises(self):
        with pytest.raises(ValueError):
            SyncOperation("invalid")

    def test_is_str_subclass(self):
        assert isinstance(SyncOperation.UPSERT, str)
        assert SyncOperation.UPSERT == "upsert"


class TestSyncLogEntry:
    def test_basic_creation(self):
        entry = SyncLogEntry(
            table_name="patterns",
            row_id="p-1",
            operation=SyncOperation.UPSERT,
            sync_version=1,
            node_id="node-a",
        )
        assert entry.table_name == "patterns"
        assert entry.row_id == "p-1"
        assert entry.operation == SyncOperation.UPSERT
        assert entry.sync_version == 1
        assert entry.node_id == "node-a"
        assert entry.created_at == ""

    def test_from_row(self):
        row = {
            "table_name": "work_items",
            "row_id": "wi-1",
            "operation": "delete",
            "sync_version": 5,
            "node_id": "node-b",
            "created_at": "2026-03-09T00:00:00",
        }
        entry = SyncLogEntry.from_row(row)
        assert entry.table_name == "work_items"
        assert entry.operation == SyncOperation.DELETE
        assert entry.sync_version == 5
        assert entry.created_at == "2026-03-09T00:00:00"

    def test_from_row_missing_created_at(self):
        row = {
            "table_name": "patterns",
            "row_id": "p-1",
            "operation": "upsert",
            "sync_version": 1,
            "node_id": "node-a",
        }
        entry = SyncLogEntry.from_row(row)
        assert entry.created_at == ""


class TestSyncState:
    def test_creation(self):
        state = SyncState(node_id="node-1", sync_version=42)
        assert state.node_id == "node-1"
        assert state.sync_version == 42
        assert state.last_sync_at == ""

    def test_with_timestamp(self):
        state = SyncState(node_id="node-1", sync_version=10, last_sync_at="2026-03-09T12:00:00")
        assert state.last_sync_at == "2026-03-09T12:00:00"


class TestSyncBatch:
    def test_empty_batch(self):
        batch = SyncBatch(table_name="patterns")
        assert batch.table_name == "patterns"
        assert batch.rows == []
        assert batch.operations == []
        assert batch.size == 0

    def test_batch_with_rows(self):
        rows = [{"id": "p-1", "title": "A"}, {"id": "p-2", "title": "B"}]
        batch = SyncBatch(table_name="patterns", rows=rows)
        assert batch.size == 2
        assert batch.rows[0]["id"] == "p-1"

    def test_batch_size_matches_rows(self):
        batch = SyncBatch(
            table_name="knowledge_entries",
            rows=[{"id": f"k-{i}"} for i in range(10)],
        )
        assert batch.size == 10


class TestSyncResult:
    def test_default_is_success(self):
        result = SyncResult()
        assert result.success is True
        assert result.tables_synced == 0
        assert result.rows_pushed == 0
        assert result.rows_pulled == 0
        assert result.conflicts_resolved == 0
        assert result.errors == []

    def test_with_errors_is_failure(self):
        result = SyncResult(errors=["connection timeout"])
        assert result.success is False
        assert len(result.errors) == 1

    def test_accumulate_stats(self):
        result = SyncResult(
            tables_synced=3,
            rows_pushed=15,
            rows_pulled=8,
            conflicts_resolved=2,
        )
        assert result.tables_synced == 3
        assert result.rows_pushed == 15
        assert result.rows_pulled == 8
        assert result.conflicts_resolved == 2
        assert result.success is True

    def test_multiple_errors(self):
        result = SyncResult(errors=["err1", "err2", "err3"])
        assert result.success is False
        assert len(result.errors) == 3
