"""
Slag-for-slag GPS-avstandsmåling for org-turneringer (ADR-103). Speiler
ADR-048s round_shot-autorisasjon (self ELLER org-admin, samme klasse som
update_hole), men org-scopet (RLS) og FK-et til
tournament_round_participant + hole_number (ikke tournament_round_hole_id
-- den raden finnes kanskje ikke ennå).
"""

import pytest
from fastapi import HTTPException

from app.auth import CurrentUser
from app.routers.tournament_round_shots import (
    ShotIn,
    ShotShareIn,
    create_tournament_round_shot,
    delete_tournament_round_shot,
    list_tournament_round_shots,
    share_tournament_round_shot,
)

from tests.conftest import (
    add_membership,
    create_course,
    create_org,
    create_player,
    create_tee,
    create_tournament,
    create_tournament_participant,
    create_tournament_round,
    create_tournament_round_participant,
    create_user,
)

_SHOT = ShotIn(
    club="Driver",
    distance_meters=210.5,
    start_method="gps",
    start_lat=59.1,
    start_lng=10.5,
    end_method="gps",
    end_lat=59.101,
    end_lng=10.501,
)


async def _setup(org_id):
    tournament_id = await create_tournament(org_id, name="Slagmåling-turnering")
    course_id = await create_course(org_id, name="Slagmålingsbanen")
    tee_id = await create_tee(org_id, course_id, name="Gul")
    round_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=1)
    return tournament_id, round_id, tee_id


async def _make_participant(org_id, tournament_id, round_id, tee_id, user_id):
    player_id = await create_player(org_id, display_name="Slag Testesen", user_id=user_id)
    tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
    rp_id = await create_tournament_round_participant(org_id, round_id, tp_id, tee_id)
    return rp_id


async def test_create_and_list_shot(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    user_id = await create_user()
    await add_membership(org_id, user_id)
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, user_id)

    created = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT,
        organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert created.shot_number == 1
    assert created.club == "Driver"
    assert created.shared_tournament_round_message_id is None

    listed = await list_tournament_round_shots(
        tournament_id, round_id, rp_id, 1, organization_id=org_id,
    )
    assert [s.id for s in listed] == [created.id]


async def test_shot_number_increments_per_hole(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    user_id = await create_user()
    await add_membership(org_id, user_id)
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, user_id)

    first = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    second = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert first.shot_number == 1
    assert second.shot_number == 2

    # Et annet hull starter på nytt fra 1.
    other_hole = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 2, _SHOT, organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert other_hole.shot_number == 1


async def test_can_measure_before_hole_score_exists(pool):
    """Kjernebegrunnelsen for datamodellen: tournament_round_hole
    opprettes FØRST ved score-innsending -- måling må fungere FØR det."""
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    user_id = await create_user()
    await add_membership(org_id, user_id)
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, user_id)

    # Ingen tournament_round_hole-rad finnes -- måling skal likevel virke.
    created = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 5, _SHOT, organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert created.shot_number == 1


async def test_non_self_non_admin_rejected(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    owner_id = await create_user()
    other_id = await create_user()
    await add_membership(org_id, owner_id, role="member")
    await add_membership(org_id, other_id, role="member")
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, owner_id)

    with pytest.raises(HTTPException) as exc_info:
        await create_tournament_round_shot(
            tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=other_id),
        )
    assert exc_info.value.status_code == 403


async def test_org_admin_can_measure_for_others(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    owner_id = await create_user()
    admin_id = await create_user()
    await add_membership(org_id, owner_id, role="member")
    await add_membership(org_id, admin_id, role="admin")
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, owner_id)

    created = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=admin_id),
    )
    assert created.shot_number == 1


async def test_delete_self_only(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    owner_id = await create_user()
    other_id = await create_user()
    await add_membership(org_id, owner_id, role="member")
    await add_membership(org_id, other_id, role="member")
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, owner_id)

    created = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=owner_id),
    )

    with pytest.raises(HTTPException) as exc_info:
        await delete_tournament_round_shot(
            tournament_id, round_id, created.id, organization_id=org_id, user=CurrentUser(user_id=other_id),
        )
    assert exc_info.value.status_code == 403

    await delete_tournament_round_shot(
        tournament_id, round_id, created.id, organization_id=org_id, user=CurrentUser(user_id=owner_id),
    )
    listed = await list_tournament_round_shots(tournament_id, round_id, rp_id, 1, organization_id=org_id)
    assert listed == []


def test_invalid_distance_rejected():
    with pytest.raises(Exception):
        ShotIn(
            club="Driver", distance_meters=0, start_method="gps",
            start_lat=59.1, start_lng=10.5, end_lat=59.101, end_lng=10.501,
        )


async def test_wrong_tournament_id_404(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    other_tournament_id = await create_tournament(org_id, name="En annen turnering")
    user_id = await create_user()
    await add_membership(org_id, user_id)
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, user_id)

    with pytest.raises(HTTPException) as exc_info:
        await create_tournament_round_shot(
            other_tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=user_id),
        )
    assert exc_info.value.status_code == 404


async def test_rls_blocks_cross_org_access(pool):
    org_a = await create_org()
    org_b = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_a)
    user_id = await create_user()
    await add_membership(org_a, user_id)
    await add_membership(org_b, user_id)
    rp_id = await _make_participant(org_a, tournament_id, round_id, tee_id, user_id)

    await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_a, user=CurrentUser(user_id=user_id),
    )

    with pytest.raises(HTTPException) as exc_info:
        await list_tournament_round_shots(tournament_id, round_id, rp_id, 1, organization_id=org_b)
    assert exc_info.value.status_code == 404


async def test_share_creates_tournament_round_message(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    user_id = await create_user()
    await add_membership(org_id, user_id)
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, user_id)

    created = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert created.shared_tournament_round_message_id is None

    shared = await share_tournament_round_shot(
        tournament_id, round_id, created.id, ShotShareIn(body="⛳ Driver, 210.5 m – hull 1"),
        organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert shared.shared_tournament_round_message_id is not None

    # Kommentartråden (ADR-102) skal nå ha en ekte melding.
    from app.routers.tournament_round_messages import list_tournament_round_messages
    messages = await list_tournament_round_messages(
        tournament_id, round_id, organization_id=org_id, user=CurrentUser(user_id=user_id),
    )
    assert any(m.id == shared.shared_tournament_round_message_id for m in messages)
    assert any(m.body == "⛳ Driver, 210.5 m – hull 1" for m in messages)


async def test_share_rejected_for_non_owner(pool):
    org_id = await create_org()
    tournament_id, round_id, tee_id = await _setup(org_id)
    owner_id = await create_user()
    other_id = await create_user()
    await add_membership(org_id, owner_id, role="member")
    await add_membership(org_id, other_id, role="member")
    rp_id = await _make_participant(org_id, tournament_id, round_id, tee_id, owner_id)

    created = await create_tournament_round_shot(
        tournament_id, round_id, rp_id, 1, _SHOT, organization_id=org_id, user=CurrentUser(user_id=owner_id),
    )
    with pytest.raises(HTTPException) as exc_info:
        await share_tournament_round_shot(
            tournament_id, round_id, created.id, ShotShareIn(body="Test"),
            organization_id=org_id, user=CurrentUser(user_id=other_id),
        )
    assert exc_info.value.status_code == 403
