import sys
from unittest.mock import Mock

import pytest
from fastapi import FastAPI
from httpx import ASGITransport, AsyncClient
from sqlalchemy import select
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine

sys.modules.setdefault('psutil', Mock())

import app.models  # noqa: F401
from app.database import Base, get_db
from app.models.brain import BrainMemory, BrainTag
from app.models.knowledge_graph import KGEdge, KGNode
from app.models.user import User
from app.routers.auth import get_current_user
from app.routers.graph import router as graph_router
from app.services.auth_service import get_password_hash
from app.services.brain_service import BrainService
from app.services.graph_service import GraphService


@pytest.fixture
async def brain_graph_env(tmp_path):
    db_path = tmp_path / 'test_brain_graph.db'
    engine = create_async_engine(f"sqlite+aiosqlite:///{db_path}", future=True)
    session_factory = async_sessionmaker(engine, expire_on_commit=False)

    async with engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

    async with session_factory() as session:
        user = User(
            email='brain-graph@example.com',
            hashed_password=get_password_hash('secret123'),
            full_name='Brain Graph Tester',
        )
        session.add(user)
        await session.flush()

        session.add_all([
            BrainMemory(
                user_id=user.id,
                memory_type='project_fact',
                title='Knowledge brain phase 1',
                content='Jarvis should learn from conversations and documents first.',
                importance=9,
                confidence=0.95,
                status='active',
                origin_source_types=['conversation', 'document'],
            ),
            BrainMemory(
                user_id=user.id,
                memory_type='user_preference',
                title='Structured delivery preference',
                content='The user prefers concise structured summaries.',
                importance=7,
                confidence=0.88,
                status='active',
                origin_source_types=['conversation'],
            ),
            BrainTag(
                user_id=user.id,
                name='knowledge-brain',
                category='topic',
                priority='important',
                score=9.5,
            ),
            BrainTag(
                user_id=user.id,
                name='conversation',
                category='source',
                priority='secondary',
                score=7.0,
            ),
        ])
        await session.commit()
        await session.refresh(user)

    async def override_get_db():
        async with session_factory() as session:
            yield session

    async def override_get_current_user():
        return user

    app = FastAPI()
    app.include_router(graph_router)
    app.dependency_overrides[get_db] = override_get_db
    app.dependency_overrides[get_current_user] = override_get_current_user

    try:
        yield session_factory, user, app
    finally:
        await engine.dispose()


@pytest.mark.asyncio
async def test_build_graph_projects_kg_nodes_and_edges_from_brain_data(brain_graph_env):
    session_factory, user, _app = brain_graph_env

    async with session_factory() as session:
        service = GraphService(session)
        await service.build_graph(user.id)

        node_result = await session.execute(
            select(KGNode).where(KGNode.user_id == user.id).order_by(KGNode.name.asc())
        )
        nodes = list(node_result.scalars().all())
        edge_result = await session.execute(select(KGEdge))
        edges = list(edge_result.scalars().all())

    node_names = [node.name for node in nodes]
    assert 'Knowledge brain phase 1' in node_names
    assert 'Structured delivery preference' in node_names
    assert 'knowledge-brain' in node_names
    assert len(edges) >= 2


@pytest.mark.asyncio
async def test_run_learning_triggers_graph_rebuild(brain_graph_env, monkeypatch):
    session_factory, user, _app = brain_graph_env
    calls: list[str] = []

    async def fake_build_graph(self, user_id, document_ids=None):
        calls.append(user_id)

    monkeypatch.setattr(GraphService, 'build_graph', fake_build_graph)

    async with session_factory() as session:
        service = BrainService(session)
        await service.run_learning(user.id)

    assert calls == [user.id]


@pytest.mark.asyncio
async def test_graph_api_returns_brain_projected_graph_after_build(brain_graph_env):
    session_factory, user, app = brain_graph_env

    async with session_factory() as session:
        service = GraphService(session)
        await service.build_graph(user.id)

    transport = ASGITransport(app=app)
    async with AsyncClient(transport=transport, base_url='http://testserver') as client:
        response = await client.get('/api/graph')

    assert response.status_code == 200
    payload = response.json()
    assert payload['stats']['node_count'] >= 3
    assert payload['stats']['edge_count'] >= 2
    assert any(node['name'] == 'Knowledge brain phase 1' for node in payload['nodes'])
    assert any(node['name'] == 'knowledge-brain' for node in payload['nodes'])
