"""
Delt testoppsett for backend-integrasjonstestene.

Kjøres ALLTID mot en scratch-database (se scripts/run_backend_tests.sh) --
aldri mot ekte teecup_db. Poolen initialiseres per test (funksjonsscope,
ikke sesjonsscope) for å unngå event loop-fallgruver med pytest-asyncio --
en full pool-oppstart tar millisekunder og suiten er liten nok til at det
ikke koster noe i praksis.

Hjelpefunksjonene under (create_user, create_org, ...) setter kun de
kolonnene testene faktisk trenger å kontrollere, med fornuftige defaults
for resten -- se \\d-utskrift av schema for hvilke felt som er NOT NULL.
"""

import uuid

import pytest_asyncio

from app import db as app_db


def new_id() -> str:
    return str(uuid.uuid4())


@pytest_asyncio.fixture
async def pool():
    await app_db.init_pool()
    yield
    await app_db.close_pool()


async def create_user(**overrides) -> str:
    """Egen tilkobling, samme begrunnelse som create_org -- app_user har
    ingen RLS (ikke org-scopet), så det er trygt/enkelt å ikke kreve en
    ferdig-åpnet `conn` fra kalleren."""
    user_id = overrides.get("id", new_id())
    display_name = overrides.get("display_name", f"Testbruker {user_id[:8]}")
    birth_date = overrides.get("birth_date")
    email = overrides.get("email", f"{user_id}@pytest.invalid")
    async with app_db.plain_connection() as conn:
        await conn.execute(
            """
            INSERT INTO app_user (id, display_name, birth_date, email)
            VALUES ($1, $2, $3, $4)
            """,
            user_id, display_name, birth_date, email,
        )
    return user_id


async def create_org(**overrides) -> str:
    """Egen tilkobling (ikke `conn`-parameter som de andre hjelperne) --
    speiler den ekte selvrefererende RLS-bootstrappen i
    routers/organizations.py::create_organization: `app.current_org` MÅ
    være satt til organisasjonens EGEN, ennå-ikke-eksisterende id FØR
    innsettingen, ellers avviser org_self-policyen den (teecup_app er
    NOSUPERUSER/NOBYPASSRLS, akkurat som i produksjon)."""
    org_id = overrides.get("id", new_id())
    name = overrides.get("name", f"Testklubb {org_id[:8]}")
    async with app_db.org_connection(org_id) as conn:
        await conn.execute("INSERT INTO organization (id, name) VALUES ($1, $2)", org_id, name)
    return org_id


async def add_membership(organization_id: str, user_id: str, role: str = "member") -> None:
    """organization_membership er org-scopet (RLS), samme begrunnelse som
    create_org over -- må skrives med riktig app.current_org satt."""
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO organization_membership (organization_id, user_id, role) VALUES ($1, $2, $3)",
            organization_id, user_id, role,
        )


async def create_round(conn, owner_user_id: str, **overrides) -> str:
    round_id = overrides.get("id", new_id())
    # course_source='teeoff' krever KUN teeoff_facility_slug/teeoff_course_id
    # (fritekst, ingen FK mot en ekte teeoff-bane) -- enklere for et testfixtur
    # enn 'custom', som (round_check-constrainten) også krever en ekte rad i
    # personal_course.
    await conn.execute(
        """
        INSERT INTO round (id, owner_user_id, course_source, teeoff_facility_slug,
                            teeoff_course_id, course_name_snapshot, tee_name_snapshot, played_at)
        VALUES ($1, $2, 'teeoff', 'test-facility', 'test-course', 'Testbanen', 'Gul', CURRENT_DATE)
        """,
        round_id, owner_user_id,
    )
    return round_id


async def create_participant(conn, round_id: str, **overrides) -> str:
    participant_id = overrides.get("id", new_id())
    user_id = overrides.get("user_id")
    is_owner = overrides.get("is_owner", True)
    course_handicap_snapshot = overrides.get("course_handicap_snapshot")
    await conn.execute(
        """
        INSERT INTO round_participant (id, round_id, user_id, is_owner, gender,
                                        tee_name_snapshot, course_handicap_snapshot)
        VALUES ($1, $2, $3, $4, 'x', 'Gul', $5)
        """,
        participant_id, round_id, user_id, is_owner, course_handicap_snapshot,
    )
    return participant_id


async def create_friendship(requester_user_id: str, addressee_user_id: str, **overrides) -> str:
    friendship_id = overrides.get("id", new_id())
    status = overrides.get("status", "accepted")
    async with app_db.plain_connection() as conn:
        await conn.execute(
            "INSERT INTO friendship (id, requester_user_id, addressee_user_id, status) VALUES ($1, $2, $3, $4)",
            friendship_id, requester_user_id, addressee_user_id, status,
        )
    return friendship_id


async def create_hole(conn, participant_id: str, hole_number: int, par: int = 4,
                       stroke_index: int = 9, **overrides) -> str:
    hole_id = overrides.get("id", new_id())
    version = overrides.get("version", 1)
    await conn.execute(
        """
        INSERT INTO round_hole (id, round_participant_id, hole_number, par, stroke_index, version)
        VALUES ($1, $2, $3, $4, $5, $6)
        """,
        hole_id, participant_id, hole_number, par, stroke_index, version,
    )
    return hole_id


# ---------------------------------------------------------------------------
# Org-turnering-kjeden (organisasjon -> turnering -> lag/bane/tee -> økt ->
# match[-deltaker]) -- brukt av test_scoring_concurrency.py. Alle disse
# tabellene er org-scopet (RLS), derfor org_connection(organization_id) i
# hver hjelper i stedet for en delt `conn`-parameter (samme mønster som
# create_org/add_membership over).
# ---------------------------------------------------------------------------

async def create_tournament(organization_id: str, **overrides) -> str:
    tournament_id = overrides.get("id", new_id())
    name = overrides.get("name", f"Testturnering {tournament_id[:8]}")
    join_code = overrides.get("join_code", tournament_id[:8])
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO tournament (id, organization_id, name, join_code) VALUES ($1, $2, $3, $4)",
            tournament_id, organization_id, name, join_code,
        )
    return tournament_id


async def create_team(organization_id: str, tournament_id: str, **overrides) -> str:
    team_id = overrides.get("id", new_id())
    name = overrides.get("name", f"Testlag {team_id[:8]}")
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO team (id, organization_id, tournament_id, name) VALUES ($1, $2, $3, $4)",
            team_id, organization_id, tournament_id, name,
        )
    return team_id


async def create_course(organization_id: str, **overrides) -> str:
    course_id = overrides.get("id", new_id())
    name = overrides.get("name", f"Testbanen {course_id[:8]}")
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO course (id, organization_id, name) VALUES ($1, $2, $3)",
            course_id, organization_id, name,
        )
    return course_id


async def create_tee(organization_id: str, course_id: str, **overrides) -> str:
    tee_id = overrides.get("id", new_id())
    name = overrides.get("name", "Gul")
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO tee (id, organization_id, course_id, name) VALUES ($1, $2, $3, $4)",
            tee_id, organization_id, course_id, name,
        )
    return tee_id


async def create_session(organization_id: str, tournament_id: str, course_id: str, **overrides) -> str:
    session_id = overrides.get("id", new_id())
    sequence = overrides.get("sequence", 1)
    format_ = overrides.get("format", "singles")
    scoring_mode = overrides.get("scoring_mode", "stroke")
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            """
            INSERT INTO session (id, organization_id, tournament_id, sequence, format,
                                  course_id, scoring_mode)
            VALUES ($1, $2, $3, $4, $5, $6, $7)
            """,
            session_id, organization_id, tournament_id, sequence, format_, course_id, scoring_mode,
        )
    return session_id


async def create_match(organization_id: str, session_id: str, team_a_id: str, team_b_id: str,
                        **overrides) -> str:
    match_id = overrides.get("id", new_id())
    sequence = overrides.get("sequence", 1)
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            """
            INSERT INTO match (id, organization_id, session_id, sequence, team_a_id, team_b_id)
            VALUES ($1, $2, $3, $4, $5, $6)
            """,
            match_id, organization_id, session_id, sequence, team_a_id, team_b_id,
        )
    return match_id


async def create_player(organization_id: str, **overrides) -> str:
    player_id = overrides.get("id", new_id())
    user_id = overrides.get("user_id")
    display_name = overrides.get("display_name", f"Testspiller {player_id[:8]}")
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO player (id, organization_id, user_id, display_name) VALUES ($1, $2, $3, $4)",
            player_id, organization_id, user_id, display_name,
        )
    return player_id


async def create_team_roster(organization_id: str, team_id: str, player_id: str, **overrides) -> str:
    roster_id = overrides.get("id", new_id())
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO team_roster (id, organization_id, team_id, player_id) VALUES ($1, $2, $3, $4)",
            roster_id, organization_id, team_id, player_id,
        )
    return roster_id


async def create_match_participant(organization_id: str, match_id: str, team_side: str,
                                    team_roster_id: str, tee_id: str, **overrides) -> str:
    participant_id = overrides.get("id", new_id())
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            """
            INSERT INTO match_participant (id, organization_id, match_id, team_side, team_roster_id, tee_id)
            VALUES ($1, $2, $3, $4, $5, $6)
            """,
            participant_id, organization_id, match_id, team_side, team_roster_id, tee_id,
        )
    return participant_id


# ---------------------------------------------------------------------------
# Individuelle/flerrunde-turneringer (ADR-037, migrasjon 040) -- brukt av
# test_flag_plant_individual_tournament.py. Samme org_connection()-mønster
# som resten av org-turnering-kjeden over.
# ---------------------------------------------------------------------------

async def create_org_hole(organization_id: str, course_id: str, hole_number: int, par: int = 4,
                           stroke_index: int | None = None, **overrides) -> str:
    hole_id = overrides.get("id", new_id())
    stroke_index = stroke_index if stroke_index is not None else hole_number
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO hole (id, organization_id, course_id, hole_number, par, stroke_index) "
            "VALUES ($1, $2, $3, $4, $5, $6)",
            hole_id, organization_id, course_id, hole_number, par, stroke_index,
        )
    return hole_id


async def create_tournament_round(organization_id: str, tournament_id: str, course_id: str,
                                   **overrides) -> str:
    round_id = overrides.get("id", new_id())
    sequence = overrides.get("sequence", 1)
    hole_config = overrides.get("hole_config", "full_18")
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO tournament_round (id, organization_id, tournament_id, sequence, course_id, hole_config) "
            "VALUES ($1, $2, $3, $4, $5, $6)",
            round_id, organization_id, tournament_id, sequence, course_id, hole_config,
        )
    return round_id


async def create_tournament_participant(organization_id: str, tournament_id: str, player_id: str,
                                         **overrides) -> str:
    participant_id = overrides.get("id", new_id())
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO tournament_participant (id, organization_id, tournament_id, player_id) "
            "VALUES ($1, $2, $3, $4)",
            participant_id, organization_id, tournament_id, player_id,
        )
    return participant_id


async def create_tournament_round_participant(organization_id: str, tournament_round_id: str,
                                               tournament_participant_id: str, tee_id: str,
                                               **overrides) -> str:
    round_participant_id = overrides.get("id", new_id())
    course_handicap = overrides.get("course_handicap", 10)
    async with app_db.org_connection(organization_id) as conn:
        await conn.execute(
            "INSERT INTO tournament_round_participant "
            "(id, organization_id, tournament_round_id, tournament_participant_id, tee_id, course_handicap) "
            "VALUES ($1, $2, $3, $4, $5, $6)",
            round_participant_id, organization_id, tournament_round_id, tournament_participant_id,
            tee_id, course_handicap,
        )
    return round_participant_id
