//
// Jellyfin lyrics. Fetches /Audio/{id}/Lyrics for the current track and
// parses the response into app.lyrics. Synced lines carry a `Start` field in
// 100-ns ticks; unsynced lines all have Start == 0.
//
// Stale-result guard: each fetch stamps app.lyrics_request_gen, and the
// callback discards itself if the generation has moved on (track changed
// before the request returned).
//

Lyric_Line :: struct {
    text:        string;
    start_ticks: s64;       // 100-ns ticks from track start; 0 for unsynced
}

Lyrics_State :: struct {
    track_id: string;       // which track these lyrics belong to
    lines:    [..] Lyric_Line;
    synced:   bool;         // true if any line has a non-zero start
    loaded:   bool;
    loading:  bool;
    request_gen: int;
}

lyrics_clear :: (s: *Lyrics_State) {
    for s.lines { free(it.text); }
    array_reset(*s.lines);
    free(s.track_id);
    s.track_id = "";
    s.synced  = false;
    s.loaded  = false;
    s.loading = false;
}

lyrics_request :: (track_id: string) {
    lyrics_clear(*app.lyrics);
    app.lyrics.track_id = copy_string(track_id);
    app.lyrics.loading  = true;
    app.lyrics.request_gen += 1;

    path := tprint("/Audio/%/Lyrics", track_id);
    log_info("lyrics: fetching for %", track_id);
    http_submit("GET", path, on_done=on_lyrics_loaded, user_data=cast(*void) app.lyrics.request_gen);
}

#scope_file

Lyric_Line_Dto :: struct {
    Text:  string;
    Start: s64;        // 100-ns ticks; absent → 0
}

Lyrics_Response :: struct {
    Lyrics: [..] Lyric_Line_Dto;
}

on_lyrics_loaded :: (task: *Http_Task) {
    gen := cast(int) task.user_data;
    if gen != app.lyrics.request_gen  return;   // user moved on; discard

    app.lyrics.loading = false;

    // 404 is the common case: track has no lyrics. Don't log it as an error.
    if task.response.status_code == 404 {
        app.lyrics.loaded = true;
        return;
    }
    if task.response.status_code == 401 { jellyfin_force_logout(); return; }
    if !task.response.ok {
        log_warn("lyrics: status=% body=%", task.response.status_code,
            slice(task.response.body, 0, min(200, task.response.body.count)));
        app.lyrics.loaded = true;
        return;
    }

    ok, parsed := Jaison.json_parse_string(task.response.body, Lyrics_Response);
    if !ok {
        log_warn("lyrics: json parse failed");
        app.lyrics.loaded = true;
        return;
    }

    any_synced := false;
    for parsed.Lyrics {
        line: Lyric_Line;
        line.text        = copy_string(it.Text);
        line.start_ticks = it.Start;
        if it.Start > 0  any_synced = true;
        array_add(*app.lyrics.lines, line);
    }
    app.lyrics.synced = any_synced;
    app.lyrics.loaded = true;
    log_info("lyrics: % lines (synced=%)", app.lyrics.lines.count, any_synced);
}
