"""
local_db.py — Lokal SQLite database for LineDance Player v0.9

Ny arkitektur:
  songs  — global katalog (synkroniseret med server, server-UUID som ID)
  files  — lokalt fil-index (kun denne maskine)
  playlist_songs — refererer til song_id + valgfri file_id
"""

import sqlite3
import logging
from contextlib import contextmanager
from pathlib import Path

logger = logging.getLogger(__name__)

DB_PATH = Path.home() / ".linedance" / "local.db"


def get_db_path() -> Path:
    return DB_PATH


@contextmanager
def get_db():
    DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    conn = sqlite3.connect(DB_PATH, timeout=10)
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
    try:
        yield conn
        conn.commit()
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()


# ── Schema ────────────────────────────────────────────────────────────────────

SCHEMA = """
-- Musik-biblioteker (mapper brugeren har tilføjet)
CREATE TABLE IF NOT EXISTS libraries (
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    path       TEXT NOT NULL UNIQUE,
    is_active  INTEGER NOT NULL DEFAULT 1,
    created_at TEXT NOT NULL DEFAULT (datetime('now'))
);

-- Global sang-katalog (synkroniseret med server)
-- ID er server-UUID. Sange uden server-ID har et lokalt UUID.
CREATE TABLE IF NOT EXISTS songs (
    id           TEXT PRIMARY KEY,
    title        TEXT NOT NULL DEFAULT '',
    artist       TEXT NOT NULL DEFAULT '',
    album        TEXT NOT NULL DEFAULT '',
    bpm          INTEGER NOT NULL DEFAULT 0,
    duration_sec INTEGER NOT NULL DEFAULT 0,
    mbid         TEXT UNIQUE,
    acoustid     TEXT,
    server_synced INTEGER NOT NULL DEFAULT 0,
    created_at   TEXT NOT NULL DEFAULT (datetime('now'))
);

-- Lokalt fil-index (kun denne maskine)
CREATE TABLE IF NOT EXISTS files (
    id               TEXT PRIMARY KEY,
    song_id          TEXT NOT NULL REFERENCES songs(id) ON DELETE CASCADE,
    local_path       TEXT NOT NULL UNIQUE,
    file_missing     INTEGER NOT NULL DEFAULT 0,
    file_format      TEXT NOT NULL DEFAULT '',
    file_modified_at TEXT NOT NULL DEFAULT '',
    extra_tags       TEXT NOT NULL DEFAULT '{}',
    created_at       TEXT NOT NULL DEFAULT (datetime('now'))
);

CREATE INDEX IF NOT EXISTS idx_files_song_id    ON files(song_id);
CREATE INDEX IF NOT EXISTS idx_files_missing    ON files(file_missing);

-- Dans-niveauer
CREATE TABLE IF NOT EXISTS dance_levels (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    sort_order  INTEGER NOT NULL,
    name        TEXT NOT NULL UNIQUE,
    description TEXT NOT NULL DEFAULT ''
);

-- Danse
-- Dans + niveau + koreograf er unik kombination
CREATE TABLE IF NOT EXISTS dances (
    id            INTEGER PRIMARY KEY AUTOINCREMENT,
    name          TEXT NOT NULL,
    level_id      INTEGER REFERENCES dance_levels(id),
    choreographer TEXT NOT NULL DEFAULT '',
    video_url     TEXT NOT NULL DEFAULT '',
    stepsheet_url TEXT NOT NULL DEFAULT '',
    notes         TEXT NOT NULL DEFAULT '',
    use_count     INTEGER NOT NULL DEFAULT 1,
    source        TEXT NOT NULL DEFAULT 'local',
    UNIQUE(name, level_id, choreographer)
);

-- Sang-dans tags
CREATE TABLE IF NOT EXISTS song_dances (
    id          TEXT PRIMARY KEY,
    song_id     TEXT NOT NULL REFERENCES songs(id) ON DELETE CASCADE,
    dance_id    INTEGER NOT NULL REFERENCES dances(id),
    dance_order INTEGER NOT NULL DEFAULT 1,
    UNIQUE(song_id, dance_id)
);

-- Alternativ-dans tags
CREATE TABLE IF NOT EXISTS song_alt_dances (
    id          TEXT PRIMARY KEY,
    song_id     TEXT NOT NULL REFERENCES songs(id) ON DELETE CASCADE,
    dance_id    INTEGER NOT NULL REFERENCES dances(id),
    note        TEXT NOT NULL DEFAULT '',
    user_rating INTEGER,  -- 1-5 stjerner, NULL = ikke vurderet
    source      TEXT NOT NULL DEFAULT 'local',
    created_at  TEXT NOT NULL DEFAULT (datetime('now')),
    UNIQUE(song_id, dance_id)
);

CREATE INDEX IF NOT EXISTS idx_song_dances       ON song_dances(song_id);
CREATE INDEX IF NOT EXISTS idx_song_alt_dances   ON song_alt_dances(song_id);
CREATE INDEX IF NOT EXISTS idx_dances_name       ON dances(name COLLATE NOCASE);
CREATE INDEX IF NOT EXISTS idx_dances_use_count  ON dances(use_count DESC);
CREATE INDEX IF NOT EXISTS idx_songs_title       ON songs(title);
CREATE INDEX IF NOT EXISTS idx_songs_artist      ON songs(artist);
CREATE INDEX IF NOT EXISTS idx_files_song_path   ON files(song_id, file_missing);

-- Playlister
CREATE TABLE IF NOT EXISTS playlists (
    id                TEXT PRIMARY KEY,
    name              TEXT NOT NULL,
    description       TEXT NOT NULL DEFAULT '',
    tags              TEXT NOT NULL DEFAULT '',
    api_project_id    TEXT UNIQUE,
    is_linked         INTEGER NOT NULL DEFAULT 0,
    server_permission TEXT NOT NULL DEFAULT 'edit',
    is_deleted        INTEGER NOT NULL DEFAULT 0,
    created_at        TEXT NOT NULL DEFAULT (datetime('now'))
);

-- Playliste-sange
CREATE TABLE IF NOT EXISTS playlist_songs (
    id               TEXT PRIMARY KEY,
    playlist_id      TEXT NOT NULL REFERENCES playlists(id) ON DELETE CASCADE,
    song_id          TEXT NOT NULL REFERENCES songs(id),
    file_id          TEXT REFERENCES files(id),
    position         INTEGER NOT NULL,
    status           TEXT NOT NULL DEFAULT 'pending',
    is_workshop      INTEGER NOT NULL DEFAULT 0,
    dance_override   TEXT NOT NULL DEFAULT '',
    alt_dance_override TEXT NOT NULL DEFAULT ''
);

CREATE INDEX IF NOT EXISTS idx_playlist_songs_playlist ON playlist_songs(playlist_id);
CREATE INDEX IF NOT EXISTS idx_playlist_songs_song     ON playlist_songs(song_id);

-- Event-state (gemmes løbende)
CREATE TABLE IF NOT EXISTS event_state (
    key   TEXT PRIMARY KEY,
    value TEXT NOT NULL
);

-- Dans-navne til autoudfyld
CREATE TABLE IF NOT EXISTS dance_names (
    id        INTEGER PRIMARY KEY AUTOINCREMENT,
    name      TEXT NOT NULL UNIQUE,
    use_count INTEGER NOT NULL DEFAULT 1
);
"""

DEFAULT_DANCE_LEVELS = [
    (10, "Absolute Beginner", "Ingen tidligere danse-erfaring kræves"),
    (20, "Beginner",          "Lidt tidligere erfaring"),
    (30, "High Beginner",     "God begynder, klar til mere"),
    (40, "Low Improver",      "Begyndende øvet"),
    (50, "Improver",          "Grundlæggende færdigheder på plads"),
    (60, "High Improver",     "Stærk øvet, næsten intermediate"),
    (70, "Low Intermediate",  "Begyndende intermediate"),
    (80, "Intermediate",      "Erfaren danser"),
    (90, "High Intermediate", "Stærk intermediate"),
    (99, "Advanced",          "Fuld beherskelse af trin og teknik"),
]


def init_db():
    """Opret tabeller og seed dance_levels hvis de mangler."""
    DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    with get_db() as conn:
        conn.executescript(SCHEMA)
        # Seed dans-niveauer
        for sort_order, name, desc in DEFAULT_DANCE_LEVELS:
            conn.execute(
                "INSERT OR IGNORE INTO dance_levels (sort_order, name, description) VALUES (?,?,?)",
                (sort_order, name, desc)
            )
    logger.info("Database initialiseret")


# ── Biblioteker ───────────────────────────────────────────────────────────────

def add_library(path: str) -> int:
    with get_db() as conn:
        cur = conn.execute(
            "INSERT OR IGNORE INTO libraries (path) VALUES (?)", (path,)
        )
        return cur.lastrowid


def get_libraries() -> list:
    with get_db() as conn:
        return conn.execute(
            "SELECT * FROM libraries WHERE is_active=1 ORDER BY path"
        ).fetchall()


def remove_library(library_id: int):
    with get_db() as conn:
        row = conn.execute(
            "SELECT path FROM libraries WHERE id=?", (library_id,)
        ).fetchone()
        if row:
            # Marker filer fra denne mappe som missing
            conn.execute(
                "UPDATE files SET file_missing=1 WHERE local_path LIKE ?",
                (row["path"] + "%",)
            )
            conn.execute("DELETE FROM libraries WHERE id=?", (library_id,))


# ── Sange ─────────────────────────────────────────────────────────────────────

def find_or_create_song(title: str, artist: str = "", album: str = "",
                         bpm: int = 0, duration_sec: int = 0,
                         mbid: str = "", acoustid: str = "",
                         song_id: str = None) -> str:
    """
    Find eksisterende sang eller opret ny. Returnerer song_id.
    Match-hierarki: server_id → mbid → acoustid → titel+artist → opret ny
    """
    import uuid as _uuid
    with get_db() as conn:
        # Match på server-ID
        if song_id:
            row = conn.execute(
                "SELECT id FROM songs WHERE id=?", (song_id,)
            ).fetchone()
            if row:
                # Opdater data hvis bedre info tilgængeligt
                conn.execute("""
                    UPDATE songs SET
                        title = CASE WHEN title='' THEN ? ELSE title END,
                        artist = CASE WHEN artist='' THEN ? ELSE artist END,
                        bpm = CASE WHEN bpm=0 THEN ? ELSE bpm END,
                        mbid = CASE WHEN mbid IS NULL AND ? != '' THEN ? ELSE mbid END,
                        acoustid = CASE WHEN acoustid IS NULL AND ? != '' THEN ? ELSE acoustid END
                    WHERE id=?
                """, (title, artist, bpm, mbid, mbid, acoustid, acoustid, song_id))
                return song_id

        # Match på MBID
        if mbid:
            row = conn.execute(
                "SELECT id FROM songs WHERE mbid=?", (mbid,)
            ).fetchone()
            if row:
                return row["id"]

        # Match på AcoustID
        if acoustid:
            row = conn.execute(
                "SELECT id FROM songs WHERE acoustid=?", (acoustid,)
            ).fetchone()
            if row:
                if mbid:
                    conn.execute(
                        "UPDATE songs SET mbid=? WHERE id=? AND mbid IS NULL",
                        (mbid, row["id"])
                    )
                return row["id"]

        # Match på titel + artist
        if title:
            row = conn.execute(
                "SELECT id FROM songs WHERE title=? AND artist=?",
                (title, artist)
            ).fetchone()
            if row:
                if mbid:
                    conn.execute(
                        "UPDATE songs SET mbid=? WHERE id=? AND mbid IS NULL",
                        (mbid, row["id"])
                    )
                return row["id"]

        # Opret ny sang
        new_id = song_id or str(_uuid.uuid4())
        conn.execute(
            "INSERT INTO songs (id, title, artist, album, bpm, duration_sec, mbid, acoustid) "
            "VALUES (?,?,?,?,?,?,?,?)",
            (new_id, title, artist, album, bpm, duration_sec,
             mbid or None, acoustid or None)
        )
        return new_id


def get_song(song_id: str) -> sqlite3.Row | None:
    with get_db() as conn:
        return conn.execute(
            "SELECT * FROM songs WHERE id=?", (song_id,)
        ).fetchone()


def update_song_bpm(song_id: str, bpm: int):
    with get_db() as conn:
        conn.execute(
            "UPDATE songs SET bpm=? WHERE id=? AND (bpm=0 OR bpm IS NULL)",
            (bpm, song_id)
        )


def update_song_mbid(song_id: str, mbid: str, acoustid: str = ""):
    with get_db() as conn:
        conn.execute(
            "UPDATE songs SET mbid=?, acoustid=? WHERE id=?",
            (mbid or None, acoustid or None, song_id)
        )


# ── Filer ─────────────────────────────────────────────────────────────────────

def upsert_file(song_id: str, local_path: str, file_format: str = "",
                file_modified_at: str = "", extra_tags: str = "{}") -> str:
    """Opret eller opdater en fil-post. Returnerer file_id."""
    import uuid as _uuid
    with get_db() as conn:
        existing = conn.execute(
            "SELECT id FROM files WHERE local_path=?", (local_path,)
        ).fetchone()
        if existing:
            conn.execute("""
                UPDATE files SET
                    song_id=?, file_missing=0,
                    file_format=?, file_modified_at=?, extra_tags=?
                WHERE id=?
            """, (song_id, file_format, file_modified_at, extra_tags, existing["id"]))
            return existing["id"]
        else:
            file_id = str(_uuid.uuid4())
            conn.execute(
                "INSERT INTO files (id, song_id, local_path, file_format, file_modified_at, extra_tags) "
                "VALUES (?,?,?,?,?,?)",
                (file_id, song_id, local_path, file_format, file_modified_at, extra_tags)
            )
            return file_id


def get_file_for_song(song_id: str) -> sqlite3.Row | None:
    """Find bedste tilgængelige fil for en sang."""
    with get_db() as conn:
        return conn.execute(
            "SELECT * FROM files WHERE song_id=? AND file_missing=0 LIMIT 1",
            (song_id,)
        ).fetchone()


def get_file(file_id: str) -> sqlite3.Row | None:
    with get_db() as conn:
        return conn.execute(
            "SELECT * FROM files WHERE id=?", (file_id,)
        ).fetchone()


def mark_file_missing(local_path: str):
    with get_db() as conn:
        conn.execute(
            "UPDATE files SET file_missing=1 WHERE local_path=?", (local_path,)
        )


def get_all_known_paths() -> set[str]:
    with get_db() as conn:
        rows = conn.execute("SELECT local_path FROM files").fetchall()
        return {r["local_path"] for r in rows}


# ── Søgning i bibliotek ───────────────────────────────────────────────────────

def search_songs(query: str, limit: int = 200) -> list:
    """Søg i sange der har en tilgængelig fil."""
    with get_db() as conn:
        pattern = f"%{query}%"
        return conn.execute("""
            SELECT s.*, f.id as file_id, f.local_path, f.file_format, f.file_missing,
                   GROUP_CONCAT(d.name, ', ') as dance_names
            FROM songs s
            JOIN files f ON f.song_id = s.id AND f.file_missing = 0
            LEFT JOIN song_dances sd ON sd.song_id = s.id
            LEFT JOIN dances d ON d.id = sd.dance_id
            WHERE s.title LIKE ? OR s.artist LIKE ? OR s.album LIKE ?
            GROUP BY s.id
            ORDER BY s.title
            LIMIT ?
        """, (pattern, pattern, pattern, limit)).fetchall()


def get_all_songs_with_files(limit: int = 5000) -> list:
    """Hent alle sange med tilgængelige filer — til biblioteksvisning."""
    with get_db() as conn:
        return conn.execute("""
            SELECT s.*, f.id as file_id, f.local_path, f.file_format, f.file_missing,
                   GROUP_CONCAT(d.name ORDER BY sd.dance_order, ', ') as dance_names
            FROM songs s
            JOIN files f ON f.song_id = s.id AND f.file_missing = 0
            LEFT JOIN song_dances sd ON sd.song_id = s.id
            LEFT JOIN dances d ON d.id = sd.dance_id
            GROUP BY s.id
            ORDER BY s.title
            LIMIT ?
        """, (limit,)).fetchall()


# ── Playlister ────────────────────────────────────────────────────────────────

def create_playlist(name: str, description: str = "", tags: str = "") -> str:
    import uuid as _uuid
    pl_id = str(_uuid.uuid4())
    with get_db() as conn:
        conn.execute(
            "INSERT INTO playlists (id, name, description, tags) VALUES (?,?,?,?)",
            (pl_id, name, description, tags)
        )
    return pl_id


def get_playlists(tag_filter: str | None = None) -> list:
    with get_db() as conn:
        if tag_filter:
            return conn.execute("""
                SELECT p.id, p.name, p.description, p.tags, p.api_project_id,
                       p.is_linked, p.server_permission, p.is_deleted, p.created_at,
                       COUNT(ps.position) as song_count
                FROM playlists p
                LEFT JOIN playlist_songs ps ON ps.playlist_id = p.id
                WHERE p.name != '__aktiv__' AND p.is_deleted = 0
                AND (p.tags LIKE ? OR p.tags LIKE ? OR p.tags LIKE ? OR p.tags = ?)
                GROUP BY p.id ORDER BY p.created_at DESC
            """, (f"{tag_filter},%", f"%, {tag_filter},%",
                  f"%, {tag_filter}", tag_filter)).fetchall()
        else:
            return conn.execute("""
                SELECT p.id, p.name, p.description, p.tags, p.api_project_id,
                       p.is_linked, p.server_permission, p.is_deleted, p.created_at,
                       COUNT(ps.position) as song_count
                FROM playlists p
                LEFT JOIN playlist_songs ps ON ps.playlist_id = p.id
                WHERE p.name != '__aktiv__' AND p.is_deleted = 0
                GROUP BY p.id ORDER BY p.created_at DESC
            """).fetchall()


def delete_playlist(playlist_id: str):
    """Soft-slet — sæt is_deleted=1."""
    with get_db() as conn:
        conn.execute(
            "UPDATE playlists SET is_deleted=1 WHERE id=?", (playlist_id,)
        )


def get_playlist_with_songs(playlist_id: str) -> dict:
    with get_db() as conn:
        playlist = conn.execute(
            "SELECT * FROM playlists WHERE id=?", (playlist_id,)
        ).fetchone()
        if not playlist:
            return {}

        songs = conn.execute("""
            SELECT ps.id as ps_id, ps.position, ps.status,
                   ps.is_workshop, ps.dance_override,
                   ps.song_id, ps.file_id,
                   s.title, s.artist, s.album, s.bpm, s.duration_sec,
                   f.local_path, f.file_format, f.file_missing
            FROM playlist_songs ps
            JOIN songs s ON s.id = ps.song_id
            LEFT JOIN files f ON f.id = ps.file_id
            WHERE ps.playlist_id = ?
            ORDER BY ps.position
        """, (playlist_id,)).fetchall()

        return {"playlist": dict(playlist), "songs": [dict(s) for s in songs]}


def add_song_to_playlist(playlist_id: str, song_id: str,
                          file_id: str | None = None,
                          position: int | None = None) -> str:
    import uuid as _uuid
    with get_db() as conn:
        if position is None:
            row = conn.execute(
                "SELECT MAX(position) as max_pos FROM playlist_songs WHERE playlist_id=?",
                (playlist_id,)
            ).fetchone()
            position = (row["max_pos"] or 0) + 1
        ps_id = str(_uuid.uuid4())
        conn.execute(
            "INSERT INTO playlist_songs (id, playlist_id, song_id, file_id, position) "
            "VALUES (?,?,?,?,?)",
            (ps_id, playlist_id, song_id, file_id, position)
        )
        return ps_id


# ── Dans-tags ─────────────────────────────────────────────────────────────────

def get_all_playlist_tags() -> list[str]:
    return get_playlist_tags()


def get_playlist_tags() -> list[str]:
    with get_db() as conn:
        rows = conn.execute(
            "SELECT tags FROM playlists WHERE tags != '' AND name != '__aktiv__' AND is_deleted=0"
        ).fetchall()
        tags = set()
        for row in rows:
            for tag in row["tags"].split(","):
                t = tag.strip().lower()
                if t:
                    tags.add(t)
        return sorted(tags)


def update_playlist_tags(playlist_id: str, tags: str):
    with get_db() as conn:
        conn.execute(
            "UPDATE playlists SET tags=? WHERE id=?", (tags, playlist_id)
        )


# ── Event-state ───────────────────────────────────────────────────────────────

def save_event_state(current_idx: int, statuses: list[str]):
    import json
    with get_db() as conn:
        conn.execute(
            "INSERT OR REPLACE INTO event_state (key,value) VALUES ('current_idx',?)",
            (str(current_idx),)
        )
        conn.execute(
            "INSERT OR REPLACE INTO event_state (key,value) VALUES ('statuses',?)",
            (json.dumps(statuses),)
        )


def load_event_state() -> tuple | None:
    import json
    with get_db() as conn:
        idx_row = conn.execute(
            "SELECT value FROM event_state WHERE key='current_idx'"
        ).fetchone()
        stat_row = conn.execute(
            "SELECT value FROM event_state WHERE key='statuses'"
        ).fetchone()
        if not idx_row or not stat_row:
            return None
        return int(idx_row["value"]), json.loads(stat_row["value"])


# ── Dans-niveauer ─────────────────────────────────────────────────────────────

def get_dance_levels() -> list:
    with get_db() as conn:
        return conn.execute(
            "SELECT * FROM dance_levels ORDER BY sort_order"
        ).fetchall()


def upsert_dance_levels(levels: list[dict]):
    with get_db() as conn:
        for lvl in levels:
            conn.execute("""
                INSERT INTO dance_levels (id, sort_order, name, description)
                VALUES (:id, :sort_order, :name, :description)
                ON CONFLICT(name) DO UPDATE SET
                    sort_order=excluded.sort_order,
                    description=excluded.description
            """, lvl)

# ── Dans-søgning (til DancePickerDialog) ─────────────────────────────────────

def get_dance_suggestions(prefix: str = "", limit: int = 20) -> list:
    """Hent dans-forslag med niveau og koreograf til autoudfyld."""
    with get_db() as conn:
        pattern = f"{prefix}%"
        return conn.execute("""
            SELECT d.id, d.name, d.level_id, dl.name as level_name,
                   d.choreographer, d.use_count
            FROM dances d
            LEFT JOIN dance_levels dl ON dl.id = d.level_id
            WHERE d.name LIKE ? COLLATE NOCASE
            ORDER BY d.use_count DESC, d.name
            LIMIT ?
        """, (pattern, limit)).fetchall()


def get_choreographer_suggestions(prefix: str = "", limit: int = 15) -> list[str]:
    """Hent koreograf-navne til autoudfyld."""
    with get_db() as conn:
        pattern = f"{prefix}%"
        rows = conn.execute("""
            SELECT DISTINCT choreographer FROM dances
            WHERE choreographer != '' AND choreographer LIKE ? COLLATE NOCASE
            ORDER BY choreographer
            LIMIT ?
        """, (pattern, limit)).fetchall()
        return [r["choreographer"] for r in rows]

# ── Dans-søgning (til DancePickerDialog og DanceInfoDialog) ──────────────────

def get_dance_suggestions(prefix: str = "", limit: int = 20) -> list:
    """Hent dans-forslag med niveau og koreograf til autoudfyld."""
    with get_db() as conn:
        pattern = f"{prefix}%"
        return conn.execute("""
            SELECT d.id, d.name, d.level_id, dl.name as level_name,
                   d.choreographer, d.use_count
            FROM dances d
            LEFT JOIN dance_levels dl ON dl.id = d.level_id
            WHERE d.name LIKE ? COLLATE NOCASE
            ORDER BY d.use_count DESC, d.name
            LIMIT ?
        """, (pattern, limit)).fetchall()


def get_choreographer_suggestions(prefix: str = "", limit: int = 15) -> list[str]:
    """Hent koreograf-navne til autoudfyld."""
    with get_db() as conn:
        pattern = f"{prefix}%"
        rows = conn.execute("""
            SELECT DISTINCT choreographer FROM dances
            WHERE choreographer != '' AND choreographer LIKE ? COLLATE NOCASE
            ORDER BY choreographer
            LIMIT ?
        """, (pattern, limit)).fetchall()
        return [r["choreographer"] for r in rows]


def get_dances_for_song(song_id: str) -> list:
    """Hent alle danse tagget på en sang med niveau og koreograf."""
    with get_db() as conn:
        rows = conn.execute("""
            SELECT d.id, d.name, d.level_id, dl.name as level_name, d.choreographer,
                   d.video_url, d.stepsheet_url, d.notes, sd.dance_order
            FROM song_dances sd
            JOIN dances d ON d.id = sd.dance_id
            LEFT JOIN dance_levels dl ON dl.id = d.level_id
            WHERE sd.song_id = ?
            ORDER BY sd.dance_order
        """, (song_id,)).fetchall()
        return [dict(r) for r in rows]


def get_alt_dances_for_song(song_id: str) -> list:
    """Hent alle alternativ-danse tagget på en sang."""
    with get_db() as conn:
        rows = conn.execute("""
            SELECT d.id, d.name, d.level_id, dl.name as level_name, d.choreographer,
                   d.video_url, d.stepsheet_url, sad.note
            FROM song_alt_dances sad
            JOIN dances d ON d.id = sad.dance_id
            LEFT JOIN dance_levels dl ON dl.id = d.level_id
            WHERE sad.song_id = ?
            ORDER BY d.name
        """, (song_id,)).fetchall()
        return [dict(r) for r in rows]

def get_or_create_dance(name: str, level_id: int | None, conn,
                         choreographer: str = "") -> int:
    """
    Find eller opret dans. Returnerer dance_id.
    Dans + niveau + koreograf er unik kombination.
    """
    choreo = choreographer or ""
    existing = conn.execute(
        "SELECT id FROM dances WHERE name=? "
        "AND (level_id=? OR (level_id IS NULL AND ? IS NULL)) "
        "AND choreographer=?",
        (name, level_id, level_id, choreo)
    ).fetchone()
    if existing:
        return existing["id"]
    cur = conn.execute(
        "INSERT INTO dances (name, level_id, choreographer) VALUES (?,?,?)",
        (name, level_id, choreo)
    )
    return cur.lastrowid

def rate_alt_dance(song_id: str, dance_id: int, rating: int | None):
    """Sæt brugerens rating (1-5) på en alternativ-dans. None = fjern rating."""
    with get_db() as conn:
        conn.execute(
            "UPDATE song_alt_dances SET user_rating=? WHERE song_id=? AND dance_id=?",
            (rating, song_id, dance_id)
        )


def get_alt_dances_for_song_with_ratings(song_id: str) -> list:
    """Hent alternativ-danse med bruger-rating og community-rating."""
    with get_db() as conn:
        rows = conn.execute("""
            SELECT d.id, d.name, d.level_id, dl.name as level_name,
                   d.choreographer, sad.note, sad.user_rating,
                   sad.source
            FROM song_alt_dances sad
            JOIN dances d ON d.id = sad.dance_id
            LEFT JOIN dance_levels dl ON dl.id = d.level_id
            WHERE sad.song_id = ?
            ORDER BY sad.user_rating DESC NULLS LAST, d.name
        """, (song_id,)).fetchall()
        return [dict(r) for r in rows]

def get_community_alts_for_song(song_id: str) -> list:
    """Hent community alternativ-danse for en sang med ratings."""
    with get_db() as conn:
        # Opret tabellen hvis den ikke eksisterer
        conn.execute(
            "CREATE TABLE IF NOT EXISTS community_alt_dances ("
            "id TEXT PRIMARY KEY, song_id TEXT NOT NULL, dance_id INTEGER NOT NULL, "
            "avg_rating REAL NOT NULL DEFAULT 0, rating_count INTEGER NOT NULL DEFAULT 0, "
            "my_rating INTEGER, UNIQUE(song_id, dance_id))"
        )
        rows = conn.execute("""
            SELECT d.id, d.name, dl.name as level_name, d.choreographer,
                   cad.avg_rating, cad.rating_count, cad.my_rating
            FROM community_alt_dances cad
            JOIN dances d ON d.id = cad.dance_id
            LEFT JOIN dance_levels dl ON dl.id = d.level_id
            WHERE cad.song_id = ?
            ORDER BY cad.avg_rating DESC
        """, (song_id,)).fetchall()
        return [dict(r) for r in rows]