"""Integration tests for app.api.v1.notes — notes CRUD."""

import uuid
import datetime

import pytest
import pytest_asyncio
from sqlalchemy import select

from app.models.literature import GlobalLiterature
from app.models.interaction import UserNote

PMID = 90000950


@pytest_asyncio.fixture
async def seed_lit(db):
    """Seed a literature item for note tests. Cleans up first."""
    from sqlalchemy import delete as sql_delete
    lit = (await db.execute(select(GlobalLiterature.id).where(GlobalLiterature.pmid == PMID))).scalar()
    if lit:
        await db.execute(sql_delete(UserNote).where(UserNote.literature_id == lit))
        await db.execute(sql_delete(GlobalLiterature).where(GlobalLiterature.id == lit))

    lit = GlobalLiterature(
        id=uuid.uuid4(), pmid=PMID, title="Notes Test Article",
        abstract="A test article for notes", authors=[{"family": "Zhao"}],
        journal="Test Journal", doi="10.1000/notes.001",
        pub_date=datetime.date(2024, 6, 1), pub_year=2024,
    )
    db.add(lit)
    await db.commit()
    return lit


@pytest_asyncio.fixture
async def auth_ctx(client):
    """Register a user and return (headers, user_id)."""
    email = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email, "password": "Test1234", "display_name": "TestUser",
    })
    assert resp.status_code == 200
    data = resp.json()
    headers = {"Authorization": f"Bearer {data['token']['access_token']}"}
    user_id = uuid.UUID(data["user"]["id"])
    return headers, user_id


# ── Unauthorized Access ──


@pytest.mark.asyncio
async def test_notes_unauthorized(client):
    """Access without auth returns 401."""
    r = await client.get("/api/v1/notes/mine")
    assert r.status_code == 401


# ── List Notes ──


@pytest.mark.asyncio
async def test_list_my_notes_empty(client, auth_ctx):
    """List my notes returns empty when none created."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/notes/mine", headers=headers)
    assert r.status_code == 200
    assert r.json() == {"items": [], "total": 0}


@pytest.mark.asyncio
async def test_list_my_notes_with_data(client, auth_ctx, seed_lit, db):
    """List my notes returns seeded notes."""
    headers, uid = auth_ctx
    n = UserNote(id=uuid.uuid4(), user_id=uid, literature_id=seed_lit.id,
                 content="My important note", is_private=True)
    db.add(n)
    await db.commit()

    r = await client.get("/api/v1/notes/mine", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert data["total"] == 1
    assert len(data["items"]) == 1
    assert data["items"][0]["content"] == "My important note"


# ── Literature Notes ──


@pytest.mark.asyncio
async def test_list_literature_notes_empty(client, auth_ctx, seed_lit):
    """List notes for literature returns empty."""
    headers, _ = auth_ctx
    r = await client.get(f"/api/v1/notes/literature/{PMID}", headers=headers)
    assert r.status_code == 200
    assert r.json() == []


@pytest.mark.asyncio
async def test_list_literature_notes_not_found(client, auth_ctx):
    """List notes for non-existent PMID returns 404."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/notes/literature/99999999", headers=headers)
    assert r.status_code == 404


# ── Create Note ──


@pytest.mark.asyncio
async def test_create_note(client, auth_ctx, seed_lit):
    """Create a note for a literature item."""
    headers, _ = auth_ctx
    r = await client.post(f"/api/v1/notes/literature/{PMID}", headers=headers, json={
        "content": "Interesting finding in this paper",
        "is_private": True,
    })
    assert r.status_code == 201
    data = r.json()
    assert data["content"] == "Interesting finding in this paper"
    assert data["is_private"] is True
    assert "id" in data
    assert data["literature_id"] == str(seed_lit.id)
    return data["id"]


@pytest.mark.asyncio
async def test_create_note_with_quote(client, auth_ctx, seed_lit):
    """Create a note with a quote."""
    headers, _ = auth_ctx
    r = await client.post(f"/api/v1/notes/literature/{PMID}", headers=headers, json={
        "content": "Key insight about EGFR",
        "quote_text": "EGFR mutations were found in 45% of patients",
    })
    assert r.status_code == 201
    assert r.json()["quote_text"] == "EGFR mutations were found in 45% of patients"


@pytest.mark.asyncio
async def test_create_note_not_found(client, auth_ctx):
    """Create note for non-existent PMID returns 404."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/notes/literature/99999999", headers=headers, json={
        "content": "Test",
    })
    assert r.status_code == 404


# ── Update Note ──


@pytest.mark.asyncio
async def test_update_note(client, auth_ctx, seed_lit):
    """Update a note's content."""
    headers, uid = auth_ctx
    r = await client.post(f"/api/v1/notes/literature/{PMID}", headers=headers, json={
        "content": "Original content",
    })
    note_id = r.json()["id"]

    r = await client.put(f"/api/v1/notes/{note_id}", headers=headers, json={
        "content": "Updated content",
        "is_private": False,
    })
    assert r.status_code == 200
    data = r.json()
    assert data["content"] == "Updated content"
    assert data["is_private"] is False


@pytest.mark.asyncio
async def test_update_note_not_found(client, auth_ctx):
    """Update non-existent note returns 404."""
    headers, _ = auth_ctx
    r = await client.put(f"/api/v1/notes/{uuid.uuid4()}", headers=headers, json={
        "content": "Updated",
    })
    assert r.status_code == 404


@pytest.mark.asyncio
async def test_update_note_invalid_id(client, auth_ctx):
    """Update with invalid note ID returns 400."""
    headers, _ = auth_ctx
    r = await client.put("/api/v1/notes/not-a-uuid", headers=headers, json={
        "content": "Updated",
    })
    assert r.status_code == 400


# ── Delete Note ──


@pytest.mark.asyncio
async def test_delete_note(client, auth_ctx, seed_lit):
    """Delete a note."""
    headers, _ = auth_ctx
    r = await client.post(f"/api/v1/notes/literature/{PMID}", headers=headers, json={
        "content": "To be deleted",
    })
    note_id = r.json()["id"]

    r = await client.delete(f"/api/v1/notes/{note_id}", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "deleted"

    r = await client.get(f"/api/v1/notes/literature/{PMID}", headers=headers)
    assert r.json() == []


@pytest.mark.asyncio
async def test_delete_note_not_found(client, auth_ctx):
    """Delete non-existent note returns 404."""
    headers, _ = auth_ctx
    r = await client.delete(f"/api/v1/notes/{uuid.uuid4()}", headers=headers)
    assert r.status_code == 404


@pytest.mark.asyncio
async def test_delete_note_invalid_id(client, auth_ctx):
    """Delete with invalid note ID returns 400."""
    headers, _ = auth_ctx
    r = await client.delete("/api/v1/notes/not-a-uuid", headers=headers)
    assert r.status_code == 400
