"""
Delt autorisasjonssjekk: to distinkte spørsmål, to funksjoner.

1. `user_is_team_captain` -- "kan brukeren sette opp/fjerne/låse LAGETS
   oppstilling?" Brukt av matches.py sin add_participant/remove_participant/
   lock_lineup, FØR noen match_participant-rad i det hele tatt finnes for
   brukeren selv (kylling-og-egg: man kan ikke kreve at brukeren ALLEREDE er
   deltaker for å få lov til å LEGGE TIL deltakere).

   2026-07-19 (Brukerroller-runden): byttet fra "hvem som helst rostret på
   laget" til kaptein (`team_roster.is_captain = true`) ELLER org-eier/admin
   -- kapteinen er nå en reell autorisasjonsrolle, ikke bare et visningsmerke.
   Se ARCHITECTURE_DECISIONS.md for hele begrunnelsen.

   Bevisst unntak: har laget INGEN utpekt kaptein ennå, godtas enhver
   rostret spiller i stedet for å låse laget helt ute -- vanlig tidlig i en
   turnering før noen har rukket å utpeke en kaptein (og en reell risiko
   funnet i eksisterende produksjonsdata: langt fra alle roster-rader har en
   kaptein i dag). Har laget FØRST fått en kaptein (`update_roster_entry`
   håndhever nå "kun én kaptein per lag"), er det utelukkende den som
   gjelder -- ingen andre rostrede spillere.

2. `user_is_match_participant` -- "kan brukeren føre/korrigere score for
   DENNE spesifikke matchen?" Brukt av scoring.py. Krever en faktisk
   match_participant-rad for brukeren i akkurat denne matchen (valgfritt
   begrenset til én side via `team_side`) -- IKKE bare rostret på laget, og
   UAVHENGIG av kapteinmerket (å være kaptein gir ikke i seg selv rett til å
   føre score for en match man selv ikke spiller).

Begge har samme org-eier/admin-fallback, av samme grunn som tidligere
(2026-07-18-runden): uten den kunne INGEN sette opp eller føre noe før minst
én spiller hadde logget inn og blitt koblet til sin player-rad. Bevisst
INGEN unntak for at organisatoren selv er rostret på MOTSTANDERLAGET i samme
turnering -- vurdert og avvist tidligere: TeeCup er et tillitsbasert verktøy
for klubber/vennegjenger, ikke en sikkerhetsgrense mot en fiendtlig
organisator (som uansett har full administrativ tilgang), og et slikt
unntak ville skapt en reell låsning.
"""

from asyncpg import Connection


async def is_org_admin(conn: Connection, organization_id: str, user_id: str) -> bool:
    # organization_membership har ingen RLS-policy (se auth.get_authorized_org
    # sin egen kommentar) -- filtrert eksplisitt på organization_id her, trygt
    # på samme tilkobling uansett hvilken app.current_org som er satt.
    result = await conn.fetchval(
        """
        SELECT EXISTS (
            SELECT 1 FROM organization_membership
            WHERE organization_id = $1 AND user_id = $2 AND role IN ('owner', 'admin')
        )
        """,
        organization_id,
        user_id,
    )
    return bool(result)


async def user_is_team_captain(
    conn: Connection, organization_id: str, team_id: str, user_id: str
) -> bool:
    has_captain = await conn.fetchval(
        "SELECT EXISTS (SELECT 1 FROM team_roster WHERE team_id = $1 AND is_captain = true)",
        team_id,
    )
    if has_captain:
        may_act = await conn.fetchval(
            """
            SELECT EXISTS (
                SELECT 1 FROM team_roster tr
                JOIN player p ON p.id = tr.player_id
                WHERE tr.team_id = $1 AND p.user_id = $2 AND tr.is_captain = true
            )
            """,
            team_id,
            user_id,
        )
    else:
        may_act = await conn.fetchval(
            """
            SELECT EXISTS (
                SELECT 1 FROM team_roster tr
                JOIN player p ON p.id = tr.player_id
                WHERE tr.team_id = $1 AND p.user_id = $2
            )
            """,
            team_id,
            user_id,
        )
    if may_act:
        return True
    return await is_org_admin(conn, organization_id, user_id)


async def user_is_match_participant(
    conn: Connection,
    organization_id: str,
    match_id: str,
    user_id: str,
    team_side: str | None = None,
) -> bool:
    is_participant = await conn.fetchval(
        """
        SELECT EXISTS (
            SELECT 1 FROM match_participant mp
            JOIN team_roster tr ON tr.id = mp.team_roster_id
            JOIN player p ON p.id = tr.player_id
            WHERE mp.match_id = $1 AND p.user_id = $2
              AND ($3::text IS NULL OR mp.team_side::text = $3)
        )
        """,
        match_id,
        user_id,
        team_side,
    )
    if is_participant:
        return True
    return await is_org_admin(conn, organization_id, user_id)


async def is_org_member(conn: Connection, organization_id: str, user_id: str) -> bool:
    """ETHVERT medlemskap (ikke bare owner/admin, se is_org_admin over) --
    brukt som den ene halvparten av OR-et som (2026-07-21, deltaker-tilgang-
    runden) erstattet den tidligere blanke get_authorized_org-sperren på
    read-endepunkter som selv ikke har noen mer finkornet sjekk (list_teams,
    list_sessions, get_scorecard) -- bevarer eksisterende org-medlemmers
    tilgang uendret, samtidig som en ikke-medlem deltaker slipper gjennom
    via user_is_tournament_participant under."""
    return bool(
        await conn.fetchval(
            "SELECT EXISTS (SELECT 1 FROM organization_membership WHERE organization_id = $1 AND user_id = $2)",
            organization_id,
            user_id,
        )
    )


async def user_is_org_player(conn: Connection, organization_id: str, user_id: str) -> bool:
    """Er brukeren koblet til NOEN spillerprofil i denne organisasjonen (ikke
    nødvendigvis en gitt turnering) -- brukt for lavsensitiv data som
    bane-/hull-info (courses.py sin list_holes), der en full
    turnering-deltaker-sjekk ville vært unødvendig presis for hva som
    faktisk beskyttes (par/stroke-index, ikke spillerdata)."""
    return bool(
        await conn.fetchval(
            "SELECT EXISTS (SELECT 1 FROM player WHERE organization_id = $1 AND user_id = $2)",
            organization_id,
            user_id,
        )
    )


async def user_is_tournament_participant(
    conn: Connection, organization_id: str, tournament_id: str, user_id: str
) -> bool:
    """Har brukerens koblede spillerprofil (ADR-017 Beslutning B) en
    registrering ELLER en rostret plass i NØYAKTIG denne turneringen?

    Flyttet hit fra registration.py 2026-07-21 (het `is_participant` der) --
    org-scopede endepunkter (matches.py/scoring.py/tournaments.py) kan ikke
    importere fra registration.py uten sirkulær import (registration.py
    importerer FRA disse tre for øvrig), men alle tre importerer allerede
    fritt fra denne (dependency-frie) modulen. registration.py importerer nå
    denne i stedet for sin egen kopi."""
    return bool(
        await conn.fetchval(
            """
            SELECT EXISTS (
                SELECT 1 FROM player p
                WHERE p.organization_id = $1 AND p.user_id = $2
                  AND (
                    EXISTS (
                        SELECT 1 FROM tournament_registration tr
                        WHERE tr.player_id = p.id AND tr.tournament_id = $3
                    )
                    OR EXISTS (
                        SELECT 1 FROM team_roster tro
                        JOIN team t ON t.id = tro.team_id
                        WHERE tro.player_id = p.id AND t.tournament_id = $3
                    )
                  )
            )
            """,
            organization_id,
            user_id,
            tournament_id,
        )
    )


async def user_is_own_tournament_participant(
    conn: Connection, organization_id: str, tournament_participant_id: str, user_id: str
) -> bool:
    """Individuelle turneringer (ADR-037): er brukeren SELV spilleren bak
    denne `tournament_participant`-raden (via `player.user_id`)?

    Mirror av `user_is_match_participant`, men uten `team_side` -- en
    individuell turnering har ingen lag å begrense siden til. Brukt av
    `individual_tournaments.py` sin `update_hole` (2026-07-30): før denne
    kunne ETHVERT org-medlem skrive score for EN HVILKEN SOM HELST
    deltaker, ikke bare sin egen -- samme klasse hull `user_is_match_
    participant` lukket for lagturneringer i ADR-023. Kun scoring er
    strammet inn her; runde-/deltaker-oppsett (opprett runde, legg til/
    fjern turnering-/rundedeltaker) forblir bevisst på org-medlemsnivå,
    samme presedens som `session`-opprettelse og `team_roster`-tilføyelse
    i tournaments.py -- ingen av dem er captain-/admin-gatet heller, kun
    selve VALGET AV HVEM SOM SPILLER EN GITT MATCH/RUNDE og selve
    scoreregistreringen er det."""
    is_self = await conn.fetchval(
        """
        SELECT EXISTS (
            SELECT 1 FROM tournament_participant tp
            JOIN player p ON p.id = tp.player_id
            WHERE tp.id = $1 AND p.user_id = $2
        )
        """,
        tournament_participant_id,
        user_id,
    )
    if is_self:
        return True
    return await is_org_admin(conn, organization_id, user_id)


async def user_is_rostered_on_team(conn: Connection, team_id: str, user_id: str) -> bool:
    """Kun for lag-chat (ADR-025 Beslutning C) -- BEVISST INGEN org-admin-
    fallback, ulikt de to funksjonene over. «Det hemmelige rommet» er ekte
    privat: kun spillere faktisk rostret på laget, aldri organisatoren, uansett
    org-rolle. Ikke gjenbruk denne for autorisasjon utenfor chat."""
    return bool(
        await conn.fetchval(
            """
            SELECT EXISTS (
                SELECT 1 FROM team_roster tr
                JOIN player p ON p.id = tr.player_id
                WHERE tr.team_id = $1 AND p.user_id = $2
            )
            """,
            team_id,
            user_id,
        )
    )
