"""Tests for Source class and file-based adapters."""
import json
import os
import tempfile

import pytest

from kanoniv.source import Source, ColumnSchema, SourceSchema


@pytest.fixture
def csv_path(tmp_path):
    p = tmp_path / "test.csv"
    p.write_text("id,name,email,age\n1,Alice,alice@example.com,30\n2,Bob,bob@example.com,25\n")
    return str(p)


@pytest.fixture
def json_path(tmp_path):
    p = tmp_path / "test.json"
    data = [
        {"id": "1", "name": "Alice", "email": "alice@example.com", "active": True},
        {"id": "2", "name": "Bob", "email": "bob@example.com", "active": False},
    ]
    p.write_text(json.dumps(data))
    return str(p)


class TestCsvSource:
    def test_from_csv_schema(self, csv_path):
        src = Source.from_csv("customers", csv_path, primary_key="id")
        schema = src.schema()
        assert isinstance(schema, SourceSchema)
        names = [c.name for c in schema.columns]
        assert "id" in names
        assert "name" in names
        assert "email" in names
        assert "age" in names

    def test_from_csv_schema_type_inference(self, csv_path):
        src = Source.from_csv("customers", csv_path)
        schema = src.schema()
        col_map = {c.name: c for c in schema.columns}
        assert col_map["age"].dtype == "number"
        assert col_map["name"].dtype == "string"

    def test_from_csv_iter_rows(self, csv_path):
        src = Source.from_csv("customers", csv_path)
        rows = list(src.iter_rows())
        assert len(rows) == 2
        assert rows[0]["name"] == "Alice"
        assert rows[1]["email"] == "bob@example.com"
        # All values are strings
        assert rows[0]["age"] == "30"

    def test_to_entities_format(self, csv_path):
        src = Source.from_csv("customers", csv_path, primary_key="id")
        entities = src.to_entities("customer")
        assert len(entities) == 2
        e = entities[0]
        assert "id" in e
        assert "tenant_id" in e
        assert "external_id" in e
        assert "entity_type" in e
        assert "data" in e
        assert "source_name" in e
        assert "last_updated" in e
        assert e["entity_type"] == "customer"
        assert e["source_name"] == "customers"
        assert e["external_id"] == "1"
        # UUID format check
        assert len(e["id"]) == 36

    def test_missing_primary_key(self, csv_path):
        src = Source.from_csv("customers", csv_path)
        entities = src.to_entities("customer")
        # Without primary_key, external_id should be auto-generated UUIDs
        for e in entities:
            assert len(e["external_id"]) == 36  # UUID length


class TestJsonSource:
    def test_from_json_basic(self, json_path):
        src = Source.from_json("accounts", json_path, primary_key="id")
        rows = list(src.iter_rows())
        assert len(rows) == 2
        assert rows[0]["name"] == "Alice"
        # Booleans should be stringified
        assert rows[0]["active"] == "True"

    def test_from_json_schema(self, json_path):
        src = Source.from_json("accounts", json_path)
        schema = src.schema()
        names = [c.name for c in schema.columns]
        assert "id" in names
        assert "active" in names

    def test_from_json_to_entities(self, json_path):
        src = Source.from_json("accounts", json_path, primary_key="id")
        entities = src.to_entities("account")
        assert len(entities) == 2
        assert entities[0]["external_id"] == "1"


class TestSourceEdgeCases:
    def test_empty_csv(self, tmp_path):
        p = tmp_path / "empty.csv"
        p.write_text("id,name\n")
        src = Source.from_csv("empty", str(p))
        rows = list(src.iter_rows())
        assert rows == []

    def test_csv_with_nulls(self, tmp_path):
        p = tmp_path / "nulls.csv"
        p.write_text("id,name,email\n1,Alice,\n2,,bob@test.com\n")
        src = Source.from_csv("nulls", str(p))
        rows = list(src.iter_rows())
        assert rows[0]["email"] == ""
        assert rows[1]["name"] == ""

    def test_json_non_array_raises(self, tmp_path):
        p = tmp_path / "bad.json"
        p.write_text('{"key": "value"}')
        src = Source.from_json("bad", str(p))
        with pytest.raises(ValueError, match="array"):
            list(src.iter_rows())
