from __future__ import annotations

import unittest

from govoplan_core.core.modules import ModuleContext, ModuleManifest
from govoplan_core.core.registry import PlatformRegistry
from govoplan_core.core.tabular_sources import (
    CAPABILITY_CONNECTORS_TABULAR_SNAPSHOT_WRITER,
    CAPABILITY_CONNECTORS_TABULAR_SOURCES,
    TabularColumn,
    TabularReadRequest,
    TabularReadResult,
    TabularSnapshotInput,
    TabularSnapshotWriter,
    TabularSource,
    TabularSourceProvider,
    TabularSourceValidationError,
    parse_tabular_csv,
    tabular_snapshot_writer,
    tabular_source_provider,
)


class _TabularProvider:
    source = TabularSource(
        ref="snapshot:source-1",
        provider="snapshot",
        source_name="monthly_cases",
        name="Monthly cases",
        schema=(TabularColumn(name="case_id", data_type="string", nullable=False),),
        fingerprint="abc123",
        row_count=1,
    )

    def list_sources(self, session, principal, *, query="", limit=100):
        del session, principal
        if query and query.casefold() not in self.source.name.casefold():
            return ()
        return (self.source,)[:limit]

    def get_source(self, session, principal, *, source_ref):
        del session, principal
        return self.source if source_ref == self.source.ref else None

    def read_source(self, session, principal, *, request):
        del session, principal
        rows = ({"case_id": "A-1"},)
        selected = rows[request.offset : request.offset + request.limit]
        return TabularReadResult(
            source=self.source,
            rows=selected,
            total_rows=len(rows),
            truncated=len(selected) < len(rows),
        )

    def create_snapshot(self, session, principal, *, snapshot):
        del session, principal, snapshot
        return self.source


class TabularSourceContractTests(unittest.TestCase):
    def test_provider_and_snapshot_writer_are_runtime_checkable(self) -> None:
        provider = _TabularProvider()

        self.assertIsInstance(provider, TabularSourceProvider)
        self.assertIsInstance(provider, TabularSnapshotWriter)

    def test_capabilities_resolve_without_importing_connectors(self) -> None:
        provider = _TabularProvider()
        registry = PlatformRegistry()
        registry.register(
            ModuleManifest(
                id="tabular_contract_test",
                name="Tabular contract test",
                version="test",
                capability_factories={
                    CAPABILITY_CONNECTORS_TABULAR_SOURCES: lambda context: provider,
                    CAPABILITY_CONNECTORS_TABULAR_SNAPSHOT_WRITER: lambda context: provider,
                },
            )
        )
        registry.configure_capability_context(ModuleContext(registry=registry, settings=object()))

        self.assertIs(provider, tabular_source_provider(registry))
        self.assertIs(provider, tabular_snapshot_writer(registry))
        self.assertIsNone(tabular_source_provider(PlatformRegistry()))

    def test_read_and_snapshot_dtos_are_immutable_and_bounded_by_callers(self) -> None:
        provider = _TabularProvider()
        request = TabularReadRequest(source_ref=provider.source.ref, limit=1)
        result = provider.read_source(object(), object(), request=request)
        snapshot = TabularSnapshotInput(
            name="Monthly cases",
            source_name="monthly_cases",
            rows=({"case_id": "A-1"},),
        )

        self.assertEqual(({"case_id": "A-1"},), result.rows)
        self.assertEqual(provider.source, provider.create_snapshot(object(), object(), snapshot=snapshot))

    def test_shared_csv_parser_preserves_identifier_zeroes_and_rejects_extra_values(self) -> None:
        rows = parse_tabular_csv(
            "case_id;amount;active\n0012;7.5;true\n\n",
            delimiter=";",
            max_rows=2,
        )

        self.assertEqual(
            ({"case_id": "0012", "amount": 7.5, "active": True},),
            rows,
        )
        with self.assertRaises(TabularSourceValidationError):
            parse_tabular_csv("id,name\n1,Ada,extra\n")


if __name__ == "__main__":
    unittest.main()
