"""Tests for the multi-chart work API (P5b): GET /api/work/{work_key}/charts +
PUT/DELETE …/preferred + POST /api/chart/{filename}/split|unsplit. HTTP surface
over the P5a grouping engine (what the P5d Charts drawer consumes)."""

import importlib
import sys

import pytest
from fastapi.testclient import TestClient


@pytest.fixture()
def server(tmp_path, monkeypatch, isolate_logging):
    monkeypatch.setenv("CONFIG_DIR", str(tmp_path))
    monkeypatch.setenv("FEEDBACK_SKIP_STARTUP_TASKS", "1")
    sys.modules.pop("server", None)
    srv = importlib.import_module("server")
    try:
        yield srv
    finally:
        conn = getattr(getattr(srv, "meta_db", None), "conn", None)
        if conn is not None:
            getattr(__import__("sys").modules.get("server"), "_join_background_db_threads", lambda: None)()
            conn.close()
        sys.modules.pop("server", None)


@pytest.fixture()
def client(server):
    return TestClient(server.app)


def _put(server, fn, title, artist, arrangements=1):
    arr = [{"name": "Lead", "index": i} for i in range(arrangements)]
    server.meta_db.put(fn, 0, 0, {"title": title, "artist": artist, "arrangements": arr})


def _wk(server, fn):
    return server.meta_db.work_key_for(fn)


def _rep(body):
    return next(c for c in body["charts"] if c["is_representative"])


# ── grouped grid row carries work_key (so the drawer can address it) ─────────

def test_grouped_row_exposes_work_key(client, server):
    _put(server, "a.archive", "Song", "Artist")
    _put(server, "b.archive", "Song", "Artist")
    row = client.get("/api/library", params={"group": 1}).json()["songs"][0]
    assert row["work_key"] == _wk(server, "a.archive")
    assert row["chart_count"] == 2


# ── GET charts ───────────────────────────────────────────────────────────────

def test_get_work_charts(client, server):
    _put(server, "a.archive", "Song", "Artist", arrangements=2)
    _put(server, "b.archive", "Song", "Artist", arrangements=1)
    body = client.get(f"/api/work/{_wk(server, 'a.archive')}/charts").json()
    assert body["count"] == 2
    assert {c["filename"] for c in body["charts"]} == {"a.archive", "b.archive"}
    assert _rep(body)["filename"] == "a.archive"          # auto-pick = most arrangements
    assert body["preferred_source"] == "auto"
    assert body["preferred_filename"] is None


def test_charts_include_best_accuracy(client, server):
    _put(server, "a.archive", "Song", "Artist")
    _put(server, "b.archive", "Song", "Artist")
    server.meta_db.record_session("a.archive", 0, score=900, accuracy=0.9)
    charts = {c["filename"]: c for c in
              client.get(f"/api/work/{_wk(server, 'a.archive')}/charts").json()["charts"]}
    assert charts["a.archive"]["best_accuracy"] == 0.9
    assert charts["b.archive"]["best_accuracy"] is None


# ── PUT / DELETE preferred ───────────────────────────────────────────────────

def test_set_preferred(client, server):
    _put(server, "a.archive", "Song", "Artist", arrangements=2)
    _put(server, "b.archive", "Song", "Artist", arrangements=1)
    wk = _wk(server, "a.archive")
    body = client.put(f"/api/work/{wk}/preferred", json={"filename": "b.archive"}).json()
    assert body["preferred_filename"] == "b.archive" and body["preferred_source"] == "user"
    assert _rep(body)["filename"] == "b.archive" and _rep(body)["is_preferred"]
    # the grouped grid now shows b as the work's card
    assert client.get("/api/library", params={"group": 1}).json()["songs"][0]["filename"] == "b.archive"


def test_set_preferred_nonmember_is_400(client, server):
    _put(server, "a.archive", "Song", "Artist")
    _put(server, "x.archive", "Other", "Artist")
    wk = _wk(server, "a.archive")
    assert client.put(f"/api/work/{wk}/preferred", json={"filename": "x.archive"}).status_code == 400
    assert client.put(f"/api/work/{wk}/preferred", json={}).status_code == 400


def test_reset_preferred_returns_to_auto(client, server):
    _put(server, "a.archive", "Song", "Artist", arrangements=2)
    _put(server, "b.archive", "Song", "Artist", arrangements=1)
    wk = _wk(server, "a.archive")
    client.put(f"/api/work/{wk}/preferred", json={"filename": "b.archive"})
    body = client.delete(f"/api/work/{wk}/preferred").json()
    assert body["preferred_source"] == "auto"
    assert _rep(body)["filename"] == "a.archive"          # auto-pick again


# ── split / unsplit ──────────────────────────────────────────────────────────

def test_split_and_unsplit_endpoints(client, server):
    _put(server, "a.archive", "Song", "Artist")
    _put(server, "b.archive", "Song", "Artist")
    wk = _wk(server, "a.archive")
    assert client.get(f"/api/work/{wk}/charts").json()["count"] == 2
    assert client.post("/api/chart/b.archive/split").json()["ok"] is True
    assert {c["filename"] for c in client.get(f"/api/work/{wk}/charts").json()["charts"]} == {"a.archive"}
    assert client.get("/api/library", params={"group": 1}).json()["total"] == 2   # two works now
    assert client.post("/api/chart/b.archive/unsplit").json()["ok"] is True
    assert client.get(f"/api/work/{wk}/charts").json()["count"] == 2              # rejoined


# ── GET chart work membership (P5d — tree/ungrouped opener resolve) ──────────

def test_chart_work_grouped_pair(client, server):
    _put(server, "a.archive", "Song", "Artist")
    _put(server, "b.archive", "Song", "Artist")
    wk = _wk(server, "a.archive")
    for fn in ("a.archive", "b.archive"):
        body = client.get(f"/api/chart/{fn}/work").json()
        assert body == {"filename": fn, "work_key": wk, "chart_count": 2,
                        "is_split": False}


def test_chart_work_singleton(client, server):
    _put(server, "a.archive", "Song", "Artist")
    body = client.get("/api/chart/a.archive/work").json()
    assert body["work_key"] == _wk(server, "a.archive")
    assert body["chart_count"] == 1


def test_chart_work_split_resolves_to_singleton_key(client, server):
    _put(server, "a.archive", "Song", "Artist")
    _put(server, "b.archive", "Song", "Artist")
    wk = _wk(server, "a.archive")
    client.post("/api/chart/b.archive/split")
    body = client.get("/api/chart/b.archive/work").json()
    # the split chart stands alone under its EFFECTIVE (split) key…
    assert body["work_key"] != wk and body["chart_count"] == 1
    # …and its key round-trips into the charts endpoint as its own work.
    # Split keys contain '#' — clients MUST URL-encode the work_key in the
    # path (the v3 client encodeURIComponent's it) or the '#' truncates the
    # URL as a fragment.
    from urllib.parse import quote
    charts = client.get(f"/api/work/{quote(body['work_key'], safe='')}/charts").json()
    assert [c["filename"] for c in charts["charts"]] == ["b.archive"]
    # the chart left behind is a singleton too
    assert client.get("/api/chart/a.archive/work").json()["chart_count"] == 1


def test_chart_work_unknown_file(client, server):
    _put(server, "a.archive", "Song", "Artist")
    body = client.get("/api/chart/nope.archive/work").json()
    assert body["work_key"] is None and body["chart_count"] == 0
