//
// Quick Connect login. The user opens any other authenticated Jellyfin
// client (web UI, mobile app, etc.), enters the 6-character code we
// display, and the server hands us an AccessToken without ever seeing the
// password — so we don't have to store one.
//
// Flow:
//   1. POST /QuickConnect/Initiate   → { Code, Secret }
//   2. show Code, poll GET /QuickConnect/Connect?secret=… every 3s
//      until { Authenticated: true }
//   3. POST /Users/AuthenticateWithQuickConnect with { Secret }
//      → standard AuthenticationResult { AccessToken, User }
//
// The poll cadence runs off the main-loop pump (jellyfin_quick_connect_pump)
// so we don't burn a thread sleeping between polls. State lives on
// Jellyfin_Client; the login view reads it to draw either the form or the
// "enter this code" panel.
//

QC_POLL_INTERVAL_S :: 3.0;

// Field names map to the Jellyfin response via JsonName notes — `Code` is
// a reserved primitive in Jai, so we rename to UserCode and instruct
// Jaison to look for "Code" in the JSON.
Quick_Connect_Result :: struct {
    Authenticated: bool;
    Secret:        string;
    UserCode:      string;  @JsonName(Code)
}

jellyfin_quick_connect_start :: () {
    c := *app.jellyfin;
    if c.qc_state != .IDLE  return;
    qc_reset(c);
    c.qc_state = .INITIATING;
    log_info("qc: initiating");
    http_submit("POST", "/QuickConnect/Initiate", on_done=on_qc_initiated);
}

jellyfin_quick_connect_cancel :: () {
    c := *app.jellyfin;
    if c.qc_state == .IDLE  return;
    log_info("qc: cancelled");
    qc_reset(c);
}

// Called from the main loop. Fires the next /QuickConnect/Connect poll once
// the cooldown has elapsed.
jellyfin_quick_connect_pump :: () {
    c := *app.jellyfin;
    if c.qc_state != .WAITING  return;
    if app.current_time < c.qc_poll_at  return;
    c.qc_poll_at = app.current_time + QC_POLL_INTERVAL_S;
    path := tprint("/QuickConnect/Connect?secret=%", c.qc_secret);
    http_submit("GET", path, on_done=on_qc_polled);
}

#scope_file

qc_reset :: (c: *Jellyfin_Client) {
    free(c.qc_code);
    free(c.qc_secret);
    c.qc_code    = "";
    c.qc_secret  = "";
    c.qc_poll_at = 0;
    c.qc_state   = .IDLE;
}

on_qc_initiated :: (task: *Http_Task) {
    c := *app.jellyfin;
    if c.qc_state != .INITIATING  return;
    if !task.response.ok {
        log_error("qc: initiate failed status=% body=%", task.response.status_code,
            slice(task.response.body, 0, min(300, task.response.body.count)));
        qc_reset(c);
        return;
    }
    ok, parsed := Jaison.json_parse_string(task.response.body, Quick_Connect_Result);
    if !ok || !parsed.UserCode || !parsed.Secret {
        log_error("qc: initiate parse failed");
        qc_reset(c);
        return;
    }
    c.qc_code    = copy_string(parsed.UserCode);
    c.qc_secret  = copy_string(parsed.Secret);
    c.qc_state   = .WAITING;
    c.qc_poll_at = app.current_time + QC_POLL_INTERVAL_S;
    log_info("qc: code=%", c.qc_code);
}

on_qc_polled :: (task: *Http_Task) {
    c := *app.jellyfin;
    if c.qc_state != .WAITING  return;   // user cancelled while in flight
    if !task.response.ok {
        // 404 is normal once the server has cleaned up an expired request;
        // anything else we treat as a transient error and keep polling.
        log_warn("qc: poll status=%", task.response.status_code);
        return;
    }
    ok, parsed := Jaison.json_parse_string(task.response.body, Quick_Connect_Result);
    if !ok  return;
    if !parsed.Authenticated  return;

    log_info("qc: approved, exchanging secret for token");
    c.qc_state = .AUTHENTICATING;
    body := tprint("{\"Secret\":\"%\"}", c.qc_secret);
    http_submit("POST", "/Users/AuthenticateWithQuickConnect", body, on_done=on_qc_authenticated);
}

on_qc_authenticated :: (task: *Http_Task) {
    c := *app.jellyfin;
    if c.qc_state != .AUTHENTICATING  return;

    if !task.response.ok {
        log_error("qc: authenticate failed status=%", task.response.status_code);
        qc_reset(c);
        return;
    }
    ok, parsed := Jaison.json_parse_string(task.response.body, Login_Response);
    if !ok || !parsed.AccessToken {
        log_error("qc: authenticate parse failed");
        qc_reset(c);
        return;
    }

    free(c.auth_token); free(c.user_id);
    c.auth_token = copy_string(parsed.AccessToken);
    c.user_id    = copy_string(parsed.User.Id);
    c.logged_in  = true;
    log_info("auth: logged in via Quick Connect as % (id=%)", parsed.User.Name, c.user_id);

    qc_reset(c);
    config_save();
    app.current_view = .LIBRARY;
    library_refresh_artists();
}
