"""Integration tests using YAML fixture files with a mock in-memory adapter."""

from __future__ import annotations

from pathlib import Path
from typing import Any

import pytest

from db_test_helpers.core import Config, set_db_data, verify_db_data

FIXTURES_DIR = str(Path(__file__).parent / "fixtures")


class InMemoryAdapter:
    """In-memory adapter that stores records keyed by collection path."""

    def __init__(self):
        self._store: dict[str, list[dict[str, Any]]] = {}
        self._auto_id_counter = 0

    def clear_group(self, group: str, parent_path: str) -> None:
        col_path = f"{parent_path}/{group}" if parent_path else group
        # Clear all entries that start with col_path (recursive)
        keys_to_delete = [k for k in self._store if k == col_path or k.startswith(col_path + "/")]
        for k in keys_to_delete:
            del self._store[k]

    def write_record(self, group: str, parent_path: str, data: dict[str, Any]) -> dict[str, Any]:
        col_path = f"{parent_path}/{group}" if parent_path else group
        data = dict(data)
        doc_id = data.pop("__document_id__", None)
        if doc_id is None:
            self._auto_id_counter += 1
            doc_id = f"auto-{self._auto_id_counter}"
        record = {"__document_id__": doc_id, **data}
        self._store.setdefault(col_path, []).append(record)
        return record

    def read_records(self, group: str, parent_path: str) -> list[dict[str, Any]]:
        col_path = f"{parent_path}/{group}" if parent_path else group
        return list(self._store.get(col_path, []))

    def build_child_path(self, parent_path: str, group: str, record: dict[str, Any]) -> str:
        col_path = f"{parent_path}/{group}" if parent_path else group
        return f"{col_path}/{record['__document_id__']}"


def _fixture(name: str) -> str:
    return f"{FIXTURES_DIR}/{name}"


class TestIntegrationBasic:
    def test_set_and_verify_basic(self):
        """seed_basic.yaml -> expected_basic.yaml (different order, nested dict/list)"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_basic.yaml"))
        verify_db_data(adapter, _fixture("expected_basic.yaml"))

    def test_set_clears_existing_data(self):
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_basic.yaml"))
        # Second set should clear existing data
        set_db_data(adapter, _fixture("seed_basic.yaml"))
        records = adapter.read_records("users", "")
        assert len(records) == 2


class TestIntegrationVar:
    def test_set_and_verify_with_var(self):
        """$var() expansion and verification"""
        adapter = InMemoryAdapter()
        config = Config(variables={"uid": "test-user-123", "username": "Alice"})
        set_db_data(adapter, _fixture("seed_with_var.yaml"), config)
        verify_db_data(adapter, _fixture("expected_with_var.yaml"), config)

    def test_var_expansion_actually_stored(self):
        """$var() should be expanded to the actual value when stored"""
        adapter = InMemoryAdapter()
        config = Config(variables={"uid": "test-user-123", "username": "Alice"})
        set_db_data(adapter, _fixture("seed_with_var.yaml"), config)
        records = adapter.read_records("users", "")
        assert len(records) == 1
        assert records[0]["__document_id__"] == "test-user-123"
        assert records[0]["name"] == "Alice"


class TestIntegrationAny:
    def test_set_and_verify_with_any(self):
        """Omitted __document_id__ gets auto ID + verify with $any() wildcard"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_with_any.yaml"))
        verify_db_data(adapter, _fixture("expected_with_any.yaml"))

    def test_omitted_id_generates_auto_id(self):
        """Omitting __document_id__ should make the adapter generate an auto ID"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_with_any.yaml"))
        records = adapter.read_records("users", "")
        assert len(records) == 2
        for r in records:
            assert r["__document_id__"].startswith("auto-")


class TestIntegrationChildren:
    def test_set_and_verify_children(self):
        """Recursive set + verify of __children__ (multi-level, different order)"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_children.yaml"))
        verify_db_data(adapter, _fixture("expected_children.yaml"))

    def test_children_data_structure(self):
        """__children__ data should be stored at the correct paths"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_children.yaml"))

        users = adapter.read_records("users", "")
        assert len(users) == 1
        assert users[0]["name"] == "Alice"

        posts = adapter.read_records("posts", "users/user-1")
        assert len(posts) == 2

        comments = adapter.read_records("comments", "users/user-1/posts/post-1")
        assert len(comments) == 2


class TestIntegrationMatchers:
    def test_verify_with_matchers(self):
        """seed_basic -> expected_with_matchers (verify using DSL matchers)"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_basic.yaml"))
        verify_db_data(adapter, _fixture("expected_with_matchers.yaml"))

    def test_verify_failure_with_wrong_expected(self, tmp_path):
        """Verification should fail with wrong expected data"""
        adapter = InMemoryAdapter()
        set_db_data(adapter, _fixture("seed_basic.yaml"))

        wrong = tmp_path / "wrong.yaml"
        wrong.write_text(
            """\
users:
  - __document_id__: "user-1"
    name: "Charlie"
    age: 30
    tags:
      - "admin"
      - "user"
    profile:
      city: "Tokyo"
      zip: "100-0001"
  - __document_id__: "user-2"
    name: "Bob"
    age: 25
    tags:
      - "user"
    profile:
      city: "Osaka"
      zip: "530-0001"
"""
        )
        with pytest.raises(AssertionError):
            verify_db_data(adapter, str(wrong))
