"""
Club & Organization Management Endpoints for Shinkan Jinkendo

Handles CRUD operations for clubs, divisions, and training groups.
"""
from typing import Any, List, Optional
from fastapi import APIRouter, HTTPException, Depends, Query
from psycopg2.extras import Json

from db import get_db, get_cursor, r2d
from tenant_context import TenantContext, get_tenant_context
from club_tenancy import (
    assert_club_member,
    can_manage_club_org,
    can_plan_in_club,
    club_ids_for_profile,
    is_platform_admin,
)

router = APIRouter(prefix="/api", tags=["clubs"])


def _blank_to_none(value: Any) -> Any:
    """Frontend sendet häufig '' für optionale Felder — ungültig für PostgreSQL TIME/INTEGER."""
    if value is None:
        return None
    if isinstance(value, str) and value.strip() == "":
        return None
    return value


def _optional_int(value: Any) -> Optional[int]:
    v = _blank_to_none(value)
    if v is None:
        return None
    try:
        i = int(v)
    except (TypeError, ValueError):
        return None
    return i if i > 0 else None


def _normalize_co_trainer_ids(val: Any) -> List[int]:
    """Listen von Profil-IDs für JSONB; ungültige Einträge werden verworfen."""
    if val is None:
        raw: List[Any] = []
    elif isinstance(val, list):
        raw = val
    else:
        raw = []
    out: List[int] = []
    seen = set()
    for x in raw:
        try:
            i = int(x)
        except (TypeError, ValueError):
            continue
        if i <= 0 or i in seen:
            continue
        seen.add(i)
        out.append(i)
    return out


def _co_trainer_ids_jsonb(val: Any) -> Json:
    """psycopg2: reine list/dict für JSONB → ProgrammingError ohne Json()."""
    return Json(_normalize_co_trainer_ids(val))


# ── List Clubs ────────────────────────────────────────────────────────
@router.get("/clubs")
def list_clubs(
    status: Optional[str] = Query(default=None),
    tenant: TenantContext = Depends(get_tenant_context),
):
    """
    Vereine: für normale Nutzer nur Mitgliedschaft-Vereine; Plattform-Admins sehen alle.
    """
    role = tenant.global_role
    profile_id = tenant.profile_id
    with get_db() as conn:
        cur = get_cursor(conn)
        query = "SELECT * FROM clubs"
        params: List[Any] = []
        conds = []

        if not is_platform_admin(role):
            cids = club_ids_for_profile(cur, profile_id)
            if not cids:
                return []
            conds.append("id IN (" + ",".join(["%s"] * len(cids)) + ")")
            params.extend(sorted(cids))

        if status:
            conds.append("status = %s")
            params.append(status)

        if conds:
            query += " WHERE " + " AND ".join(conds)

        query += " ORDER BY name"

        cur.execute(query, params)
        rows = cur.fetchall()
        return [r2d(r) for r in rows]


# ── Öffentliches Vereinsverzeichnis (Registrierung / Antrag ohne Mitgliedschaft) ──
@router.get("/clubs/public-directory")
def public_club_directory():
    """Aktive Vereine zur Auswahl bei Registrierung oder Beitrittsantrag (nur id, name, Kürzel)."""
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute(
            """
            SELECT id, name, abbreviation
            FROM clubs
            WHERE status = 'active'
            ORDER BY name
            """
        )
        return [r2d(r) for r in cur.fetchall()]


# ── Aktive Mitglieder für Trainer-/Co-Trainer-Auswahl (Vereinsmitglied) ──
@router.get("/clubs/{club_id}/members/directory")
def club_members_directory(club_id: int, tenant: TenantContext = Depends(get_tenant_context)):
    """id + name für alle aktiven Mitglieder; E-Mail nur für Plattform-Admin oder Vereinsadmin (Org-Verwaltung)."""
    profile_id = tenant.profile_id
    role = tenant.global_role
    with get_db() as conn:
        cur = get_cursor(conn)
        if not is_platform_admin(role):
            assert_club_member(cur, profile_id, club_id)
        cur.execute("SELECT 1 FROM clubs WHERE id = %s", (club_id,))
        if not cur.fetchone():
            raise HTTPException(404, "Verein nicht gefunden")
        cur.execute(
            """
            SELECT p.id, p.name, p.email
            FROM club_members cm
            INNER JOIN profiles p ON p.id = cm.profile_id
            WHERE cm.club_id = %s AND cm.status = 'active'
            ORDER BY COALESCE(p.name, ''), p.email
            """,
            (club_id,),
        )
        rows = [r2d(r) for r in cur.fetchall()]
        show_email = is_platform_admin(role) or can_manage_club_org(cur, profile_id, club_id, role)
        if not show_email:
            for d in rows:
                d["email"] = None
        return rows


# ── Get Club ──────────────────────────────────────────────────────────
@router.get("/clubs/{club_id}")
def get_club(club_id: int, tenant: TenantContext = Depends(get_tenant_context)):
    """Get club by ID with divisions and groups – nur Mitglied oder Plattform-Admin."""
    profile_id = tenant.profile_id
    role = tenant.global_role
    with get_db() as conn:
        cur = get_cursor(conn)
        if not is_platform_admin(role):
            assert_club_member(cur, profile_id, club_id)

        # Get club
        cur.execute("SELECT * FROM clubs WHERE id = %s", (club_id,))
        club = cur.fetchone()

        if not club:
            raise HTTPException(404, "Verein nicht gefunden")

        club = r2d(club)

        # Get divisions
        cur.execute(
            """
            SELECT * FROM divisions
            WHERE club_id = %s
            ORDER BY name
        """,
            (club_id,),
        )
        club["divisions"] = [r2d(r) for r in cur.fetchall()]

        # Get training groups
        cur.execute(
            """
            SELECT g.*,
                p.name as trainer_name
            FROM training_groups g
            LEFT JOIN profiles p ON g.trainer_id = p.id
            WHERE g.club_id = %s
            ORDER BY g.weekday, g.time_start
        """,
            (club_id,),
        )
        club["training_groups"] = [r2d(r) for r in cur.fetchall()]

        return club


# ── Create Club ───────────────────────────────────────────────────────
@router.post("/clubs")
def create_club(data: dict, tenant: TenantContext = Depends(get_tenant_context)):
    """Neuen Verein anlegen – nur Plattform-Admin; Pflicht: primary_admin_profile_id (Hauptverwalter:in)."""
    role = tenant.global_role
    if not is_platform_admin(role):
        raise HTTPException(403, "Nur Plattform-Administratoren dürfen neue Vereine anlegen")

    name = data.get("name")
    primary_admin_profile_id = data.get("primary_admin_profile_id")
    if not name:
        raise HTTPException(400, "Name ist Pflichtfeld")
    if not primary_admin_profile_id:
        raise HTTPException(400, "primary_admin_profile_id ist Pflichtfeld (Hauptverwalter:in)")

    try:
        aid = int(primary_admin_profile_id)
    except (TypeError, ValueError):
        raise HTTPException(400, "primary_admin_profile_id ungültig")

    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("SELECT id FROM profiles WHERE id = %s", (aid,))
        if not cur.fetchone():
            raise HTTPException(404, "Profil für Hauptverwalter nicht gefunden")

        cur.execute(
            """
            INSERT INTO clubs (name, abbreviation, description, status, primary_admin_profile_id)
            VALUES (%s, %s, %s, %s, %s)
            RETURNING id
        """,
            (
                name,
                data.get("abbreviation"),
                data.get("description"),
                data.get("status", "active"),
                aid,
            ),
        )

        club_id = cur.fetchone()["id"]

        cur.execute(
            """
            INSERT INTO club_members (profile_id, club_id, status)
            VALUES (%s, %s, 'active')
            ON CONFLICT (profile_id, club_id)
            DO UPDATE SET status = 'active', updated_at = NOW()
            RETURNING id
        """,
            (aid, club_id),
        )
        cm_id = cur.fetchone()["id"]
        for rc in ("club_admin", "trainer"):
            cur.execute(
                """
                INSERT INTO club_member_roles (club_member_id, role_code)
                VALUES (%s, %s)
                ON CONFLICT (club_member_id, role_code) DO NOTHING
            """,
                (cm_id, rc),
            )

        conn.commit()

    return get_club(club_id, tenant)


# ── Update Club ───────────────────────────────────────────────────────
@router.put("/clubs/{club_id}")
def update_club(club_id: int, data: dict, tenant: TenantContext = Depends(get_tenant_context)):
    """Verein bearbeiten – Plattform-Admin oder Vereinsadmin."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        # Check existence
        cur.execute("SELECT id FROM clubs WHERE id = %s", (club_id,))
        if not cur.fetchone():
            raise HTTPException(404, "Verein nicht gefunden")

        if not can_manage_club_org(cur, profile_id, club_id, role):
            raise HTTPException(403, "Keine Berechtigung für diesen Verein")

        # Nur Plattform-Admin darf primary_admin_profile_id ändern
        if "primary_admin_profile_id" in data and data["primary_admin_profile_id"] is not None:
            if not is_platform_admin(role):
                raise HTTPException(403, "Nur Plattform-Admins dürfen den Hauptverwalter ändern")

        # Update
        cur.execute(
            """
            UPDATE clubs SET
                name = COALESCE(%s, name),
                abbreviation = COALESCE(%s, abbreviation),
                description = COALESCE(%s, description),
                status = COALESCE(%s, status),
                primary_admin_profile_id = COALESCE(%s, primary_admin_profile_id),
                updated_at = NOW()
            WHERE id = %s
        """,
            (
                data.get("name"),
                data.get("abbreviation"),
                data.get("description"),
                data.get("status"),
                data.get("primary_admin_profile_id"),
                club_id,
            ),
        )

        conn.commit()

    return get_club(club_id, tenant)


# ── Delete Club ───────────────────────────────────────────────────────
@router.delete("/clubs/{club_id}")
def delete_club(club_id: int, tenant: TenantContext = Depends(get_tenant_context)):
    """Delete club (superadmin only)."""
    role = tenant.global_role
    if role != 'superadmin':
        raise HTTPException(403, "Nur Superadmins dürfen Vereine löschen")

    with get_db() as conn:
        cur = get_cursor(conn)

        # Check existence
        cur.execute("SELECT id FROM clubs WHERE id = %s", (club_id,))
        if not cur.fetchone():
            raise HTTPException(404, "Verein nicht gefunden")

        # Delete (CASCADE handles divisions and groups)
        cur.execute("DELETE FROM clubs WHERE id = %s", (club_id,))
        conn.commit()

    return {"ok": True}


# ── List Divisions ────────────────────────────────────────────────────
@router.get("/divisions")
def list_divisions(
    club_id: Optional[int] = Query(default=None),
    tenant: TenantContext = Depends(get_tenant_context),
):
    """Sparten – ohne Admin-Rechte nur in eigenen Vereinen sichtbar."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        mine = club_ids_for_profile(cur, profile_id)
        if not is_platform_admin(role) and not mine:
            return []

        query = """
            SELECT d.*, c.name as club_name
            FROM divisions d
            LEFT JOIN clubs c ON d.club_id = c.id
        """
        where = []
        params = []

        if club_id is not None:
            where.append("d.club_id = %s")
            params.append(club_id)
            if not is_platform_admin(role) and club_id not in mine:
                return []

        if not is_platform_admin(role):
            where.append(
                "d.club_id IN (" + ",".join(["%s"] * len(mine)) + ")"
            )
            params.extend(sorted(mine))

        if where:
            query += " WHERE " + " AND ".join(where)

        query += " ORDER BY d.name"

        cur.execute(query, params)
        rows = cur.fetchall()
        return [r2d(r) for r in rows]


# ── Create Division ───────────────────────────────────────────────────
@router.post("/divisions")
def create_division(data: dict, tenant: TenantContext = Depends(get_tenant_context)):
    """Create new division – Vereinsadmin / Plattform-Admin."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    club_id = data.get("club_id")
    name = data.get("name")

    if not club_id or not name:
        raise HTTPException(400, "club_id und name sind Pflichtfelder")

    with get_db() as conn:
        cur = get_cursor(conn)

        if not can_manage_club_org(cur, profile_id, int(club_id), role):
            raise HTTPException(403, "Keine Berechtigung, Sparten in diesem Verein anzulegen")

        # Check club exists
        cur.execute("SELECT id FROM clubs WHERE id = %s", (club_id,))
        if not cur.fetchone():
            raise HTTPException(404, "Verein nicht gefunden")

        # Insert
        cur.execute(
            """
            INSERT INTO divisions (club_id, name, focus_area)
            VALUES (%s, %s, %s)
            RETURNING id
        """,
            (
                club_id,
                name,
                data.get("focus_area"),
            ),
        )

        division_id = cur.fetchone()["id"]
        conn.commit()

        # Return created division
        cur.execute(
            """
            SELECT d.*, c.name as club_name
            FROM divisions d
            LEFT JOIN clubs c ON d.club_id = c.id
            WHERE d.id = %s
        """,
            (division_id,),
        )
        return r2d(cur.fetchone())


# ── Update Division ───────────────────────────────────────────────────
@router.put("/divisions/{division_id}")
def update_division(division_id: int, data: dict, tenant: TenantContext = Depends(get_tenant_context)):
    """Update division – Vereinsadmin / Plattform-Admin."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute("SELECT id, club_id FROM divisions WHERE id = %s", (division_id,))
        div = cur.fetchone()
        if not div:
            raise HTTPException(404, "Sparte nicht gefunden")

        if not can_manage_club_org(cur, profile_id, div["club_id"], role):
            raise HTTPException(403, "Keine Berechtigung")

        cur.execute(
            """
            UPDATE divisions SET
                name = %s,
                focus_area = %s,
                updated_at = NOW()
            WHERE id = %s
        """,
            (
                data.get("name"),
                data.get("focus_area"),
                division_id,
            ),
        )

        conn.commit()

        cur.execute(
            """
            SELECT d.*, c.name as club_name
            FROM divisions d
            LEFT JOIN clubs c ON d.club_id = c.id
            WHERE d.id = %s
        """,
            (division_id,),
        )
        return r2d(cur.fetchone())


# ── Delete Division ───────────────────────────────────────────────────
@router.delete("/divisions/{division_id}")
def delete_division(division_id: int, tenant: TenantContext = Depends(get_tenant_context)):
    """Delete division – Vereinsadmin / Plattform-Admin."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute("SELECT id, club_id FROM divisions WHERE id = %s", (division_id,))
        div = cur.fetchone()
        if not div:
            raise HTTPException(404, "Sparte nicht gefunden")

        if not can_manage_club_org(cur, profile_id, div["club_id"], role):
            raise HTTPException(403, "Keine Berechtigung")

        cur.execute("DELETE FROM divisions WHERE id = %s", (division_id,))
        conn.commit()

    return {"ok": True}


# ── List Training Groups ──────────────────────────────────────────────
@router.get("/groups")
def list_training_groups(
    club_id: Optional[int] = Query(default=None),
    division_id: Optional[int] = Query(default=None),
    status: Optional[str] = Query(default=None),
    tenant: TenantContext = Depends(get_tenant_context),
):
    """
    Trainingsgruppen – ohne Plattform-Admin nur in eigenen Vereinen.
    """
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        mine = club_ids_for_profile(cur, profile_id)
        if not is_platform_admin(role) and not mine:
            return []

        query = """
            SELECT g.*,
                c.name as club_name,
                d.name as division_name,
                p.name as trainer_name
            FROM training_groups g
            LEFT JOIN clubs c ON g.club_id = c.id
            LEFT JOIN divisions d ON g.division_id = d.id
            LEFT JOIN profiles p ON g.trainer_id = p.id
        """

        where = []
        params = []

        if club_id is not None:
            where.append("g.club_id = %s")
            params.append(club_id)
            if not is_platform_admin(role) and club_id not in mine:
                return []

        if division_id is not None:
            where.append("g.division_id = %s")
            params.append(division_id)

        if status:
            where.append("g.status = %s")
            params.append(status)

        if not is_platform_admin(role):
            where.append("g.club_id IN (" + ",".join(["%s"] * len(mine)) + ")")
            params.extend(sorted(mine))

        if where:
            query += " WHERE " + " AND ".join(where)

        query += " ORDER BY g.weekday, g.time_start"

        cur.execute(query, params)
        rows = cur.fetchall()
        return [r2d(r) for r in rows]


# ── Get Training Group ────────────────────────────────────────────────
@router.get("/groups/{group_id}")
def get_training_group(group_id: int, tenant: TenantContext = Depends(get_tenant_context)):
    """Trainingsgruppe – nur mit Vereinszugriff."""
    profile_id = tenant.profile_id
    role = tenant.global_role
    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute(
            """
            SELECT g.*,
                c.name as club_name,
                d.name as division_name,
                p.name as trainer_name
            FROM training_groups g
            LEFT JOIN clubs c ON g.club_id = c.id
            LEFT JOIN divisions d ON g.division_id = d.id
            LEFT JOIN profiles p ON g.trainer_id = p.id
            WHERE g.id = %s
        """,
            (group_id,),
        )

        group = cur.fetchone()

        if not group:
            raise HTTPException(404, "Trainingsgruppe nicht gefunden")

        cid = group["club_id"]
        if not is_platform_admin(role):
            assert_club_member(cur, profile_id, cid)

        return r2d(group)


# ── Create Training Group ─────────────────────────────────────────────
@router.post("/groups")
def create_training_group(data: dict, tenant: TenantContext = Depends(get_tenant_context)):
    """Trainingsgruppe anlegen – Mitglied mit Planungs-/Admin-Rolle im Verein."""
    profile_id = tenant.profile_id
    role = tenant.global_role
    if role not in ("admin", "superadmin", "trainer", "user"):
        raise HTTPException(403, "Keine Berechtigung, Trainingsgruppen anzulegen")

    club_id = data.get("club_id")
    name = data.get("name")

    if not club_id or not name:
        raise HTTPException(400, "club_id und name sind Pflichtfelder")

    raw_tid = data.get("trainer_id")
    if role in ("trainer", "user"):
        parsed_tid = _optional_int(raw_tid)
        trainer_id = parsed_tid if parsed_tid is not None else profile_id
    else:
        trainer_id = _optional_int(raw_tid)

    division_id = _optional_int(data.get("division_id"))
    co_json = _co_trainer_ids_jsonb(data.get("co_trainer_ids"))

    with get_db() as conn:
        cur = get_cursor(conn)

        if not can_plan_in_club(cur, profile_id, int(club_id), role):
            raise HTTPException(403, "Keine Berechtigung für diesen Verein")

        # Check club exists
        cur.execute("SELECT id FROM clubs WHERE id = %s", (club_id,))
        if not cur.fetchone():
            raise HTTPException(404, "Verein nicht gefunden")

        # Insert
        cur.execute(
            """
            INSERT INTO training_groups (
                club_id, division_id, name, focus, level, age_group,
                weekday, time_start, time_end, location,
                trainer_id, co_trainer_ids, status
            ) VALUES (
                %s, %s, %s, %s, %s, %s,
                %s, %s, %s, %s,
                %s, %s::jsonb, %s
            ) RETURNING id
        """,
            (
                int(club_id),
                division_id,
                name,
                _blank_to_none(data.get("focus")),
                _blank_to_none(data.get("level")),
                _blank_to_none(data.get("age_group")),
                _blank_to_none(data.get("weekday")),
                _blank_to_none(data.get("time_start")),
                _blank_to_none(data.get("time_end")),
                _blank_to_none(data.get("location")),
                trainer_id,
                co_json,
                data.get("status") or "active",
            ),
        )

        group_id = cur.fetchone()["id"]
        conn.commit()

    return get_training_group(group_id, tenant)


# ── Update Training Group ─────────────────────────────────────────────
@router.put("/groups/{group_id}")
def update_training_group(group_id: int, data: dict, tenant: TenantContext = Depends(get_tenant_context)):
    """Update training group – Vereinsadmin, Plattform-Admin oder zugewiesene Trainer."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute(
            "SELECT trainer_id, co_trainer_ids, club_id FROM training_groups WHERE id = %s",
            (group_id,),
        )
        row = cur.fetchone()
        if not row:
            raise HTTPException(404, "Trainingsgruppe nicht gefunden")

        co_trainers = row["co_trainer_ids"] or []
        club_id = row["club_id"]

        allowed = role in ("admin", "superadmin")
        if not allowed:
            allowed = can_manage_club_org(cur, profile_id, club_id, role)
        if not allowed:
            allowed = row["trainer_id"] == profile_id or profile_id in co_trainers

        if not allowed:
            raise HTTPException(403, "Keine Berechtigung")

        co_json = _co_trainer_ids_jsonb(data.get("co_trainer_ids"))

        cur.execute(
            """
            UPDATE training_groups SET
                name = %s,
                division_id = %s,
                focus = %s,
                level = %s,
                age_group = %s,
                weekday = %s,
                time_start = %s,
                time_end = %s,
                location = %s,
                trainer_id = %s,
                co_trainer_ids = %s::jsonb,
                status = %s,
                updated_at = NOW()
            WHERE id = %s
        """,
            (
                data.get("name"),
                _optional_int(data.get("division_id")),
                _blank_to_none(data.get("focus")),
                _blank_to_none(data.get("level")),
                _blank_to_none(data.get("age_group")),
                _blank_to_none(data.get("weekday")),
                _blank_to_none(data.get("time_start")),
                _blank_to_none(data.get("time_end")),
                _blank_to_none(data.get("location")),
                _optional_int(data.get("trainer_id")),
                co_json,
                data.get("status") or "active",
                group_id,
            ),
        )

        conn.commit()

    return get_training_group(group_id, tenant)


# ── Delete Training Group ─────────────────────────────────────────────
@router.delete("/groups/{group_id}")
def delete_training_group(group_id: int, tenant: TenantContext = Depends(get_tenant_context)):
    """Delete training group – Vereinsadmin oder Plattform-Admin."""
    profile_id = tenant.profile_id
    role = tenant.global_role

    with get_db() as conn:
        cur = get_cursor(conn)

        cur.execute("SELECT id, club_id FROM training_groups WHERE id = %s", (group_id,))
        row = cur.fetchone()
        if not row:
            raise HTTPException(404, "Trainingsgruppe nicht gefunden")

        if not can_manage_club_org(cur, profile_id, row["club_id"], role):
            raise HTTPException(403, "Keine Berechtigung")

        cur.execute("DELETE FROM training_groups WHERE id = %s", (group_id,))
        conn.commit()

    return {"ok": True}
