from __future__ import annotations

from typing import Any

from pydantic import BaseModel

from sync_state_machine.datasource.api.handler import BaseApiHandler
from sync_state_machine.datasource.task_result import TaskResult


class _UpdateSchema(BaseModel):
    id: str
    required_value: float
    optional_text: str | None = None


class _DummyApiHandler(BaseApiHandler[BaseModel]):
    _node_type = "dummy"
    _node_class = object  # type: ignore[assignment]
    _schema = BaseModel

    async def create_all(self, nodes):
        return [TaskResult.success(node_id="dummy")]

    async def update_all(self, nodes):
        return [TaskResult.success(node_id="dummy")]

    async def delete_all(self, nodes):
        return [TaskResult.success(node_id="dummy")]

    async def load(self):
        return []


def test_get_update_schema_diff_falls_back_to_schema_fields_when_origin_is_incomplete() -> None:
    handler = _DummyApiHandler()

    diff = handler.get_update_schema_diff(
        _UpdateSchema,
        {"id": "node-1", "required_value": 4.0, "optional_text": "ok"},
        {"id": "node-1"},
        node_id="node-1",
    )

    assert diff == {"required_value": 4.0, "optional_text": "ok"}


def test_build_update_request_data_uses_same_schema_projection_as_diff() -> None:
    handler = _DummyApiHandler()

    payload = handler.build_update_request_data(
        {"id": "node-1", "required_value": 4.0, "optional_text": "ok", "ignored": "x"},
        {"id": "node-1"},
        schema=_UpdateSchema,
    )

    assert payload == {"id": "node-1", "required_value": 4.0, "optional_text": "ok"}


def test_schema_changed_fields_matches_update_payload_comparison() -> None:
    handler = _DummyApiHandler()

    changed_fields = handler._schema_changed_fields(
        _UpdateSchema,
        {"id": "node-1", "required_value": 4.0, "optional_text": "ok"},
        {"id": "node-1", "required_value": 4.0},
    )

    assert changed_fields == ["optional_text"]


def test_update_schema_payload_filters_empty_values() -> None:
    handler = _DummyApiHandler()

    payload = handler._get_update_schema_payload(
        _UpdateSchema,
        {"id": "node-1", "required_value": 4.0, "optional_text": ""},
        require_valid=False,
    )

    assert payload == {"id": "node-1", "required_value": 4.0}


def test_get_update_schema_diff_skips_schema_when_new_data_is_still_incomplete() -> None:
    handler = _DummyApiHandler()

    diff = handler.get_update_schema_diff(
        _UpdateSchema,
        {"id": "node-1"},
        {"id": "node-1", "required_value": 2.0},
        node_id="node-1",
    )

    assert diff is None