"""
Rangefinder-koordinater for offisielle (TeeOff-koblede) baner (migrasjon
079, ADR-081). To lag med tester:

1. `get_target_points()`/`resolve_match_course_key()` -- de delte
   byggeklossene direkte, uavhengig av hvilket av de tre kallestedene
   som bruker dem.
2. Integrasjon mot alle tre endepunktene (frittstående runde, individuell
   org-turnering, lag-matchplay) + den nye skrive-veien for offisielle
   baners koordinater i courses.py.
"""

from fastapi import HTTPException
import pytest

from app.auth import CurrentUser
from app.hole_history import resolve_match_course_key
from app.target_points import get_target_points
from app.routers.courses import CoursePointIn, list_course_coordinates, replace_course_coordinates
from app.routers.individual_tournaments import get_tournament_round_hole_target_points
from app.routers.rounds import get_round_hole_target_points
from app.routers.scoring import get_match_hole_target_points

from tests.conftest import (
    add_membership,
    create_course,
    create_match,
    create_org,
    create_org_hole,
    create_participant,
    create_player,
    create_round,
    create_session,
    create_team,
    create_team_roster,
    create_tee,
    create_tournament,
    create_tournament_participant,
    create_tournament_round,
    create_tournament_round_participant,
    create_user,
)

import app.db as app_db


# ---------------------------------------------------------------------------
# 1. get_target_points() / resolve_match_course_key()
# ---------------------------------------------------------------------------

async def test_get_target_points_returns_empty_for_none_key(pool):
    async with app_db.plain_connection() as conn:
        points = await get_target_points(conn, None, 1)
    assert points == []


async def test_get_target_points_teeoff_branch(pool):
    async with app_db.plain_connection() as conn:
        await conn.execute(
            """
            INSERT INTO teeoff_course_coordinate
                (external_course_ref, hole_number, poi_type, location, side_fairway, latitude, longitude)
            VALUES ('slug:99', 5, 'green', 'middle', 'center', 59.1, 10.4)
            """
        )
        points = await get_target_points(conn, ("teeoff", "slug", "99"), 5)
        other_hole = await get_target_points(conn, ("teeoff", "slug", "99"), 6)
    assert len(points) == 1
    assert points[0].poi_type == "green"
    assert points[0].latitude == 59.1
    assert other_hole == []


async def test_get_target_points_golfapi_branch(pool):
    async with app_db.plain_connection() as conn:
        await conn.execute(
            """
            INSERT INTO golfapi_course (course_id, club_id, club_name, course_name, num_holes, num_coordinates)
            VALUES ('gapi-1', 'club-1', 'Testklubb', 'Bane', 18, 1)
            """
        )
        await conn.execute(
            """
            INSERT INTO golfapi_course_coordinate
                (golfapi_course_id, hole_number, poi_type, location, side_fairway, latitude, longitude)
            VALUES ('gapi-1', 3, 'water', 'front', 'left', 59.2, 10.5)
            """
        )
        points = await get_target_points(conn, ("golfapi", "gapi-1"), 3)
    assert len(points) == 1
    assert points[0].poi_type == "water"


async def test_resolve_match_course_key_custom_course_returns_none(pool):
    user_id = await create_user()
    org_id = await create_org()
    await add_membership(org_id, user_id, role="member")
    tournament_id = await create_tournament(org_id)
    course_id = await create_course(org_id)  # source defaults to 'custom'
    team_a = await create_team(org_id, tournament_id)
    team_b = await create_team(org_id, tournament_id)
    session_id = await create_session(org_id, tournament_id, course_id)
    match_id = await create_match(org_id, session_id, team_a, team_b)

    async with app_db.org_connection(org_id) as conn:
        key = await resolve_match_course_key(conn, match_id)
    assert key is None


async def test_resolve_match_course_key_official_course(pool):
    org_id = await create_org()
    tournament_id = await create_tournament(org_id)
    course_id = await create_course(org_id)
    async with app_db.org_connection(org_id) as conn:
        await conn.execute(
            "UPDATE course SET source = 'official', external_course_ref = 'slug-x:42' WHERE id = $1",
            course_id,
        )
    team_a = await create_team(org_id, tournament_id)
    team_b = await create_team(org_id, tournament_id)
    session_id = await create_session(org_id, tournament_id, course_id)
    match_id = await create_match(org_id, session_id, team_a, team_b)

    async with app_db.org_connection(org_id) as conn:
        key = await resolve_match_course_key(conn, match_id)
    assert key == ("teeoff", "slug-x", "42")


async def test_resolve_match_course_key_international_course(pool):
    org_id = await create_org()
    tournament_id = await create_tournament(org_id)
    course_id = await create_course(org_id)
    async with app_db.org_connection(org_id) as conn:
        await conn.execute(
            "UPDATE course SET source = 'international', external_course_ref = 'gapi-77' WHERE id = $1",
            course_id,
        )
    team_a = await create_team(org_id, tournament_id)
    team_b = await create_team(org_id, tournament_id)
    session_id = await create_session(org_id, tournament_id, course_id)
    match_id = await create_match(org_id, session_id, team_a, team_b)

    async with app_db.org_connection(org_id) as conn:
        key = await resolve_match_course_key(conn, match_id)
    assert key == ("golfapi", "gapi-77")


async def test_resolve_match_course_key_nonexistent_match_returns_none(pool):
    org_id = await create_org()
    async with app_db.org_connection(org_id) as conn:
        key = await resolve_match_course_key(conn, "00000000-0000-0000-0000-000000000000")
    assert key is None


# ---------------------------------------------------------------------------
# 2. Integrasjon: de tre GET-endepunktene + skrive-veien i courses.py.
# ---------------------------------------------------------------------------

async def test_round_target_points_teeoff_round_now_returns_points(pool):
    """Regresjonsbevis for refaktoreringen: FØR migrasjon 079 returnerte
    dette endepunktet [] for EN HVER teeoff-sourcet frittstående runde,
    uansett koordinatdata. Nå slår den opp via samme bane-bro som
    hole_history.py."""
    user_id = await create_user()
    async with app_db.plain_connection() as conn:
        round_id = await create_round(conn, user_id)  # teeoff/test-facility/test-course
        await create_participant(conn, round_id, user_id=user_id, is_owner=True)
        await conn.execute(
            """
            INSERT INTO teeoff_course_coordinate
                (external_course_ref, hole_number, poi_type, location, side_fairway, latitude, longitude)
            VALUES ('test-facility:test-course', 4, 'tee_front', 'middle', 'center', 59.0, 10.0)
            """
        )
    points = await get_round_hole_target_points(round_id, 4, user=CurrentUser(user_id=user_id))
    assert len(points) == 1
    assert points[0].poi_type == "tee_front"

    empty = await get_round_hole_target_points(round_id, 5, user=CurrentUser(user_id=user_id))
    assert empty == []


async def test_tournament_round_target_points_official_course(pool):
    user_id = await create_user()
    org_id = await create_org()
    await add_membership(org_id, user_id, role="member")
    tournament_id = await create_tournament(org_id)
    course_id = await create_course(org_id)
    async with app_db.org_connection(org_id) as conn:
        await conn.execute(
            "UPDATE course SET source = 'official', external_course_ref = 'tourn-slug:7' WHERE id = $1",
            course_id,
        )
        await conn.execute(
            """
            INSERT INTO teeoff_course_coordinate
                (external_course_ref, hole_number, poi_type, location, side_fairway, latitude, longitude)
            VALUES ('tourn-slug:7', 9, 'rock', 'front', 'center', 59.3, 10.6)
            """
        )
    for n in range(1, 19):
        await create_org_hole(org_id, course_id, hole_number=n)
    tournament_round_id = await create_tournament_round(org_id, tournament_id, course_id)

    points = await get_tournament_round_hole_target_points(
        tournament_id, tournament_round_id, 9, organization_id=org_id,
    )
    assert len(points) == 1
    assert points[0].poi_type == "rock"

    with pytest.raises(HTTPException) as exc_info:
        await get_tournament_round_hole_target_points(
            "00000000-0000-0000-0000-000000000000", tournament_round_id, 9, organization_id=org_id,
        )
    assert exc_info.value.status_code == 404


async def test_match_target_points_official_course_and_access_control(pool):
    member_id = await create_user()
    outsider_id = await create_user()
    org_id = await create_org()
    await add_membership(org_id, member_id, role="member")
    tournament_id = await create_tournament(org_id)
    course_id = await create_course(org_id)
    async with app_db.org_connection(org_id) as conn:
        await conn.execute(
            "UPDATE course SET source = 'official', external_course_ref = 'match-slug:3' WHERE id = $1",
            course_id,
        )
        await conn.execute(
            """
            INSERT INTO teeoff_course_coordinate
                (external_course_ref, hole_number, poi_type, location, side_fairway, latitude, longitude)
            VALUES ('match-slug:3', 2, 'water', 'back', 'right', 59.4, 10.7)
            """
        )
    team_a = await create_team(org_id, tournament_id)
    team_b = await create_team(org_id, tournament_id)
    session_id = await create_session(org_id, tournament_id, course_id)
    match_id = await create_match(org_id, session_id, team_a, team_b)

    points = await get_match_hole_target_points(
        match_id, 2, organization_id=org_id, user=CurrentUser(user_id=member_id),
    )
    assert len(points) == 1
    assert points[0].poi_type == "water"

    with pytest.raises(HTTPException) as exc_info:
        await get_match_hole_target_points(
            match_id, 2, organization_id=org_id, user=CurrentUser(user_id=outsider_id),
        )
    assert exc_info.value.status_code == 403


async def test_course_coordinates_write_path_requires_official_and_fully_replaces(pool):
    org_id = await create_org()
    course_id = await create_course(org_id)  # 'custom' by default

    # 400 på ikke-offisiell bane.
    with pytest.raises(HTTPException) as exc_info:
        await replace_course_coordinates(
            course_id,
            [CoursePointIn(hole_number=1, poi_type="green", location="middle", side_fairway="center",
                            latitude=59.0, longitude=10.0)],
            organization_id=org_id,
        )
    assert exc_info.value.status_code == 400

    # GET returnerer stille tom liste for ikke-offisiell bane (ikke feil).
    assert await list_course_coordinates(course_id, organization_id=org_id) == []

    async with app_db.org_connection(org_id) as conn:
        await conn.execute(
            "UPDATE course SET source = 'official', external_course_ref = 'write-slug:1' WHERE id = $1",
            course_id,
        )

    first_set = [
        CoursePointIn(hole_number=1, poi_type="green", location="front", side_fairway="center",
                      latitude=59.0, longitude=10.0),
        CoursePointIn(hole_number=1, poi_type="water", location="back", side_fairway="left",
                      latitude=59.01, longitude=10.01),
    ]
    result = await replace_course_coordinates(course_id, first_set, organization_id=org_id)
    assert len(result) == 2

    # Ny PUT ERSTATTER hele settet -- ikke et tillegg.
    second_set = [
        CoursePointIn(hole_number=2, poi_type="rock", location="middle", side_fairway="right",
                      latitude=59.02, longitude=10.02),
    ]
    result = await replace_course_coordinates(course_id, second_set, organization_id=org_id)
    assert len(result) == 1
    assert result[0].hole_number == 2
    assert result[0].poi_type == "rock"

    read_back = await list_course_coordinates(course_id, organization_id=org_id)
    assert len(read_back) == 1
    assert read_back[0].poi_type == "rock"
