# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# Licensed under the MIT License

"""Tests for Memory Tree dashboard API endpoints (Sprint 8)."""

import json
from unittest.mock import MagicMock, patch

import pytest

from familiar.core.memory_tree import MemoryTree


@pytest.fixture
def tree(tmp_path):
    return MemoryTree(db_path=tmp_path / "api_test.db")


@pytest.fixture
def client(tree, monkeypatch):
    """Create a Flask test client with auth key and mocked MemoryTree."""
    monkeypatch.setenv("FAMILIAR_DASHBOARD_KEY", "test-key-123")

    from familiar.dashboard.app import app

    app.config["TESTING"] = True

    with patch("familiar.dashboard.blueprints.memory._get_memory_tree", return_value=tree):
        with app.test_client() as c:
            # Wrap client to inject auth header automatically
            _orig_open = c.open

            def _authed_open(*args, **kwargs):
                headers = kwargs.get("headers", {})
                if isinstance(headers, dict):
                    headers["X-API-Key"] = "test-key-123"
                    headers["Origin"] = "http://localhost:5000"
                else:
                    headers = {"X-API-Key": "test-key-123", "Origin": "http://localhost:5000"}
                kwargs["headers"] = headers
                return _orig_open(*args, **kwargs)

            c.open = _authed_open
            yield c


class TestMemoryTreeListAPI:
    def test_list_empty(self, client, tree):
        resp = client.get("/api/memory/tree?tier=working")
        data = resp.get_json()
        assert resp.status_code == 200
        assert data["entries"] == []
        assert "stats" in data

    def test_list_with_entries(self, client, tree):
        tree.store(title="Test Fact", body="hello", tier="working")
        resp = client.get("/api/memory/tree?tier=working")
        data = resp.get_json()
        assert len(data["entries"]) == 1
        assert data["entries"][0]["title"] == "Test Fact"

    def test_list_all_tiers(self, client, tree):
        tree.store(title="W", body="w", tier="working")
        tree.store(title="L", body="l", tier="long")
        resp = client.get("/api/memory/tree?tier=all")
        data = resp.get_json()
        assert len(data["entries"]) == 2


class TestMemoryTreeGetAPI:
    def test_get_existing(self, client, tree):
        entry = tree.store(title="Get Me", body="content")
        resp = client.get(f"/api/memory/tree/{entry.memory_id}")
        data = resp.get_json()
        assert resp.status_code == 200
        assert data["entry"]["title"] == "Get Me"

    def test_get_not_found(self, client):
        resp = client.get("/api/memory/tree/nonexistent-id")
        assert resp.status_code == 404


class TestMemoryTreeStoreAPI:
    def test_store_new(self, client, tree):
        resp = client.post(
            "/api/memory/tree",
            data=json.dumps({"title": "New Fact", "body": "body", "tier": "working"}),
            content_type="application/json",
        )
        assert resp.status_code == 201
        data = resp.get_json()
        assert data["entry"]["title"] == "New Fact"

    def test_store_no_title(self, client):
        resp = client.post(
            "/api/memory/tree",
            data=json.dumps({"body": "orphan"}),
            content_type="application/json",
        )
        assert resp.status_code == 400


class TestMemoryTreeUpdateAPI:
    def test_update_existing(self, client, tree):
        entry = tree.store(title="Old Title", body="old")
        resp = client.put(
            f"/api/memory/tree/{entry.memory_id}",
            data=json.dumps({"title": "New Title"}),
            content_type="application/json",
        )
        assert resp.status_code == 200
        assert resp.get_json()["entry"]["title"] == "New Title"

    def test_update_not_found(self, client):
        resp = client.put(
            "/api/memory/tree/nonexistent",
            data=json.dumps({"title": "X"}),
            content_type="application/json",
        )
        assert resp.status_code == 404


class TestMemoryTreeDeleteAPI:
    def test_delete_existing(self, client, tree):
        entry = tree.store(title="Deletable", body="x")
        resp = client.delete(
            f"/api/memory/tree/{entry.memory_id}",
            data=json.dumps({"reason": "test"}),
            content_type="application/json",
        )
        assert resp.status_code == 200
        assert not tree.get(entry.memory_id).is_valid()

    def test_delete_not_found(self, client):
        resp = client.delete(
            "/api/memory/tree/nonexistent",
            data=json.dumps({}),
            content_type="application/json",
        )
        assert resp.status_code == 404


class TestMemoryTreeSearchAPI:
    def test_search_found(self, client, tree):
        tree.store(title="Banana Recipe", body="Mix banana and flour")
        resp = client.get("/api/memory/tree/search?q=banana")
        data = resp.get_json()
        assert resp.status_code == 200
        assert len(data["results"]) >= 1

    def test_search_no_query(self, client):
        resp = client.get("/api/memory/tree/search")
        assert resp.status_code == 400


class TestMemoryTreeGraphAPI:
    def test_graph_existing(self, client, tree):
        e1 = tree.store(title="A", body="a")
        e2 = tree.store(title="B", body="b")
        tree.link(e1.memory_id, e2.memory_id, "related")
        resp = client.get(f"/api/memory/tree/{e1.memory_id}/graph")
        data = resp.get_json()
        assert resp.status_code == 200
        assert len(data["nodes"]) == 2
        assert len(data["edges"]) == 1

    def test_graph_not_found(self, client):
        resp = client.get("/api/memory/tree/nonexistent/graph")
        assert resp.status_code == 404


class TestMemoryTreeHistoryAPI:
    def test_history_with_versions(self, client, tree):
        e1 = tree.store(title="Fact v1", body="original")
        e2 = tree.update_fact(e1.memory_id, new_body="updated")
        resp = client.get(f"/api/memory/tree/{e2.memory_id}/history")
        data = resp.get_json()
        assert resp.status_code == 200
        assert len(data["history"]) >= 2

    def test_history_not_found(self, client):
        resp = client.get("/api/memory/tree/nonexistent/history")
        assert resp.status_code == 404
