"""
ADR-063: @-tagging i runde-feedens innlegg/kommentarer -- kun faktiske
relasjoner (rundedeltakere/venner) er taggbare, aldri fritekstsøk i hele
brukerbasen. Kaller de faktiske router-funksjonene direkte.
"""

import pytest
from fastapi import HTTPException

from app import db as app_db
from app.auth import CurrentUser
from app.routers.round_messages import (
    CommentIn,
    TagIn,
    list_taggable_people,
    post_round_message,
    post_round_message_comment,
)

from tests.conftest import (
    create_friendship,
    create_participant,
    create_round,
    create_user,
)


async def _setup_round_with_owner_and_friend():
    owner_id = await create_user()
    friend_id = await create_user()
    stranger_id = await create_user()  # verken medspiller eller venn
    await create_friendship(owner_id, friend_id)
    async with app_db.plain_connection() as conn:
        round_id = await create_round(conn, owner_id)
        await create_participant(conn, round_id, user_id=owner_id, is_owner=True)
    return owner_id, friend_id, stranger_id, round_id


async def test_taggable_people_includes_friend_not_stranger(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    candidates = await list_taggable_people(round_id, q="", user=CurrentUser(user_id=owner_id))
    ids = {c["user_id"] for c in candidates}
    assert friend_id in ids
    assert stranger_id not in ids
    assert owner_id not in ids  # kan ikke tagge seg selv


async def test_taggable_people_includes_round_participant(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    co_player_id = await create_user()
    async with app_db.plain_connection() as conn:
        await create_participant(conn, round_id, user_id=co_player_id, is_owner=False)
    candidates = await list_taggable_people(round_id, q="", user=CurrentUser(user_id=owner_id))
    ids = {c["user_id"] for c in candidates}
    assert co_player_id in ids


async def test_taggable_people_filters_by_query(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    friend_name = (await list_taggable_people(round_id, q="", user=CurrentUser(user_id=owner_id)))[0]["display_name"]
    matched = await list_taggable_people(round_id, q=friend_name[:3], user=CurrentUser(user_id=owner_id))
    assert any(c["user_id"] == friend_id for c in matched)
    unmatched = await list_taggable_people(round_id, q="zzz-ingen-treff-zzz", user=CurrentUser(user_id=owner_id))
    assert unmatched == []


async def test_post_message_with_valid_tag_creates_tag_and_notification(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    body = "God runde med @Venn i dag"
    tag_json = f'[{{"user_id": "{friend_id}", "start_index": 14, "end_index": 18}}]'

    out = await post_round_message(round_id, body=body, image=None, tags=tag_json, client_message_id=None, user=CurrentUser(user_id=owner_id))

    assert len(out.tags) == 1
    assert out.tags[0].user_id == friend_id

    async with app_db.plain_connection() as conn:
        notif = await conn.fetchrow(
            "SELECT type, message FROM notification WHERE user_id = $1", friend_id
        )
    assert notif is not None
    assert notif["type"] == "round"


async def test_post_message_with_stranger_tag_is_silently_dropped(pool):
    """Ugyldig tag (mot noen som verken er medspiller eller venn) skal
    IKKE avvise hele innlegget -- kun selve tag-forsøket forkastes."""
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    body = "Hyggelig runde"
    tag_json = f'[{{"user_id": "{stranger_id}", "start_index": 0, "end_index": 9}}]'

    out = await post_round_message(round_id, body=body, image=None, tags=tag_json, client_message_id=None, user=CurrentUser(user_id=owner_id))

    assert out.body == body
    assert out.tags == []
    async with app_db.plain_connection() as conn:
        notif = await conn.fetchval("SELECT count(*) FROM notification WHERE user_id = $1", stranger_id)
    assert notif == 0


async def test_post_message_with_out_of_bounds_tag_is_dropped(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    body = "Kort"
    tag_json = f'[{{"user_id": "{friend_id}", "start_index": 0, "end_index": 999}}]'

    out = await post_round_message(round_id, body=body, image=None, tags=tag_json, client_message_id=None, user=CurrentUser(user_id=owner_id))
    assert out.tags == []


async def test_post_comment_with_valid_tag(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    message = await post_round_message(round_id, body="Innlegg", image=None, tags=None, client_message_id=None, user=CurrentUser(user_id=owner_id))

    comment_body = "Bra jobba @Venn"
    payload = CommentIn(body=comment_body, tags=[TagIn(user_id=friend_id, start_index=10, end_index=14)])
    comment = await post_round_message_comment(round_id, message.id, payload, user=CurrentUser(user_id=owner_id))

    assert len(comment.tags) == 1
    assert comment.tags[0].user_id == friend_id
    assert comment.tags[0].display_name


async def test_cannot_tag_self(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    body = "Meg selv"
    tag_json = f'[{{"user_id": "{owner_id}", "start_index": 0, "end_index": 4}}]'
    out = await post_round_message(round_id, body=body, image=None, tags=tag_json, client_message_id=None, user=CurrentUser(user_id=owner_id))
    assert out.tags == []


# ---------------------------------------------------------------------------
# Idempotens for offline-kø-synk (ADR-028-tillegget, migrasjon 073,
# 2026-08-14) -- se round_messages.py::post_round_message.
# ---------------------------------------------------------------------------

async def test_repeated_client_message_id_returns_same_message_not_a_duplicate(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    client_message_id = "11111111-1111-1111-1111-111111111111"

    first = await post_round_message(
        round_id, body="Fra banen", image=None, tags=None,
        client_message_id=client_message_id, user=CurrentUser(user_id=owner_id),
    )
    second = await post_round_message(
        round_id, body="Fra banen", image=None, tags=None,
        client_message_id=client_message_id, user=CurrentUser(user_id=owner_id),
    )

    assert first.id == second.id
    async with app_db.plain_connection() as conn:
        count = await conn.fetchval(
            "SELECT count(*) FROM round_message WHERE round_id = $1 AND client_message_id = $2",
            round_id, client_message_id,
        )
    assert count == 1


async def test_different_client_message_ids_create_separate_messages(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()

    first = await post_round_message(
        round_id, body="Første", image=None, tags=None,
        client_message_id="22222222-2222-2222-2222-222222222222", user=CurrentUser(user_id=owner_id),
    )
    second = await post_round_message(
        round_id, body="Andre", image=None, tags=None,
        client_message_id="33333333-3333-3333-3333-333333333333", user=CurrentUser(user_id=owner_id),
    )

    assert first.id != second.id


async def test_missing_client_message_id_still_works(pool):
    owner_id, friend_id, stranger_id, round_id = await _setup_round_with_owner_and_friend()
    out = await post_round_message(
        round_id, body="Uten idempotens-nøkkel", image=None, tags=None,
        client_message_id=None, user=CurrentUser(user_id=owner_id),
    )
    assert out.body == "Uten idempotens-nøkkel"
