from __future__ import annotations

from uuid import uuid4

import pytest
from fastapi.testclient import TestClient

from tests.orchestrator_dual_harness import OrchestratorDualHubHarness, make_enterprise


@pytest.fixture()
def dual_harness(client: TestClient):
    enterprise_id = make_enterprise(client)
    harness = OrchestratorDualHubHarness(client, enterprise_id)
    hub_a, hub_b = harness.pair_hubs()
    return harness, hub_a, hub_b


def test_hub_a_push_reaches_hub_b(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    record_id = "comp-dual-001"
    event = harness.make_component_event(
        hub_a, record_id, name="Corn A", version=1, content_hash="hash-a1"
    )
    harness.push_from(hub_a, [event])
    harness.drain()
    row = harness.get_catalog(hub_b, "component", record_id)
    assert row is not None
    assert row["name"] == "Corn A"
    assert row["content_hash"] == "hash-a1"


def test_hub_b_push_back_to_hub_a(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    record_id = "comp-dual-002"
    harness.push_from(
        hub_a,
        [harness.make_component_event(hub_a, record_id, name="V1", version=1, content_hash="h1")],
    )
    harness.drain()
    harness.push_from(
        hub_b,
        [harness.make_component_event(hub_b, record_id, name="V2 from B", version=2, content_hash="h2")],
    )
    harness.drain()
    row_a = harness.get_catalog(hub_a, "component", record_id)
    assert row_a is not None
    assert row_a["content_hash"] == "h2"
    assert row_a["name"] == "V2 from B"


def test_idempotent_replay(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    record_id = "comp-dual-003"
    event = harness.make_component_event(
        hub_a, record_id, name="Once", version=1, content_hash="once1", event_id="evt-fixed-003"
    )
    harness.push_from(hub_a, [event])
    harness.push_from(hub_a, [event])
    harness.drain()
    orch = harness.get_orchestrator_catalog("component", record_id)
    assert orch is not None
    assert orch["name"] == "Once"
    row_b = harness.get_catalog(hub_b, "component", record_id)
    assert row_b is not None
    assert row_b["content_hash"] == "once1"


def test_ack_cursor_no_double_apply(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    record_id = "comp-dual-004"
    event = harness.make_component_event(
        hub_a, record_id, name="Ack safe", version=1, content_hash="ack1", event_id="evt-ack-004"
    )
    harness.push_from(hub_a, [event])
    events1 = harness.pull_for(hub_b)
    assert len(events1) == 1
    events2 = harness.pull_for(hub_b)
    assert len(events2) == 0
    row = harness.get_catalog(hub_b, "component", record_id)
    assert row is not None
    assert row["name"] == "Ack safe"


def test_no_conflict_single_editor(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    record_id = "comp-dual-005"
    harness.push_from(
        hub_a,
        [harness.make_component_event(hub_a, record_id, name="Solo", version=1, content_hash="solo1")],
    )
    harness.drain()
    from app.modules.sync import repository as repo

    conflicts = repo.list_conflicts(harness.enterprise_id, "pending")
    assert conflicts == []
    row = harness.get_orchestrator_catalog("component", record_id)
    assert row is not None
    assert row["name"] == "Solo"


def test_conflict_dual_edit(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    record_id = "comp-dual-006"
    base = {"name": "Base", "type": "grain", "dry_matter": 50.0}
    harness.push_from(
        hub_a,
        [
            {
                "event_id": str(uuid4()),
                "seq": 1,
                "domain": "global",
                "table": "component",
                "record_id": record_id,
                "action": "upsert",
                "version": 1,
                "content_hash": "base-hash",
                "payload": {**base, "id": record_id, "version": 1, "content_hash": "base-hash"},
                "emitted_at": "2026-07-15T10:00:00Z",
                "origin_site_id": hub_a.hub_site_id,
            }
        ],
    )
    harness.drain()
    harness.push_from(
        hub_b,
        [
            {
                "event_id": str(uuid4()),
                "seq": 1,
                "domain": "global",
                "table": "component",
                "record_id": record_id,
                "action": "upsert",
                "version": 1,
                "content_hash": "b-conflict-hash",
                "payload": {
                    "name": "B edit",
                    "type": "grain",
                    "dry_matter": 60.0,
                    "id": record_id,
                    "version": 1,
                    "content_hash": "b-conflict-hash",
                },
                "emitted_at": "2026-07-15T10:01:00Z",
                "origin_site_id": hub_b.hub_site_id,
            }
        ],
    )
    from app.modules.sync import repository as repo

    conflicts = repo.list_conflicts(harness.enterprise_id, "pending")
    assert len(conflicts) == 1
    orch = harness.get_orchestrator_catalog("component", record_id)
    assert orch is not None
    assert orch["content_hash"] == "base-hash"


def test_period_recipes_composite_record_id(client: TestClient, dual_harness):
    harness, hub_a, hub_b = dual_harness
    period_id = "737a78a5-492a-4a84-94f3-18b4d7f21666"
    recipe_id = "07fd17cf-22c3-44fe-92db-46363b24cee0"
    record_id = f"{period_id}:{recipe_id}"
    event = {
        "event_id": str(uuid4()),
        "seq": 1,
        "domain": "global",
        "table": "period_recipes",
        "record_id": record_id,
        "action": "upsert",
        "version": 1,
        "content_hash": "pr1",
        "payload": {
            "period_id": period_id,
            "recipe_id": recipe_id,
            "order": 1,
            "version": 1,
            "content_hash": "pr1",
        },
        "emitted_at": "2026-07-15T12:00:00Z",
        "origin_site_id": hub_a.hub_site_id,
    }
    harness.push_from(hub_a, [event])
    harness.drain()
    row = harness.get_catalog(hub_b, "period_recipes", record_id)
    assert row is not None
    assert row.get("order") == 1
