//
// HTTP client for Jellyfin. Wraps libcurl with a write-to-builder callback so
// each request returns the response body as a string.
//
// We follow Jellyfin's auth scheme: every request carries an
// `Authorization: MediaBrowser ...` header. Authenticated requests append a
// Token field; the login request omits it.
//
// Note: the legacy `X-Emby-Authorization` header is being removed in
// Jellyfin 12.0 and admins on 10.11+ can already disable it. Use the modern
// `Authorization` header.
//
// The DeviceId is per-install and persisted in config.json. Jellyfin
// permits only one active access token per DeviceId — sharing one across
// installs (or hardcoding a constant) silently revokes prior tokens the
// next time anyone logs in.
//

CLIENT_NAME    :: "Jellyfin Celica Music Player";
CLIENT_VERSION :: "0.0.1";
DEVICE_NAME    :: "Celica";

Jellyfin_Client :: struct {
    server_url:  string;
    username:    string;
    password:    string;

    auth_token:    string;
    user_id:       string;
    device_id:     string;     // persisted; one per install (see config.jai)
    logged_in:     bool;
    login_pending: bool;

    // Quick Connect — see quick_connect.jai. Lives on the client so the
    // login view can render its current state.
    qc_state:   Quick_Connect_State;
    qc_code:    string;        // 6-char user-facing code shown in the UI
    qc_secret:  string;        // server token we poll with
    qc_poll_at: float64;       // app.current_time when next poll is due
}

Quick_Connect_State :: enum {
    IDLE;
    INITIATING;
    WAITING;
    AUTHENTICATING;
}

jellyfin_client_init :: (c: *Jellyfin_Client) {
    // Curl globals are managed lazily on first request.
}

jellyfin_client_shutdown :: (c: *Jellyfin_Client) {
    // Nothing yet.
}

//
// Request helpers
//

Http_Response :: struct {
    status_code: int;
    body:        string;
    ok:          bool;
}

build_auth_header :: (c: *Jellyfin_Client) -> string {
    if c.logged_in && c.auth_token {
        return tprint(
            "MediaBrowser Client=\"%\", Device=\"%\", DeviceId=\"%\", Version=\"%\", Token=\"%\"",
            CLIENT_NAME, DEVICE_NAME, c.device_id, CLIENT_VERSION, c.auth_token);
    }
    return tprint(
        "MediaBrowser Client=\"%\", Device=\"%\", DeviceId=\"%\", Version=\"%\"",
        CLIENT_NAME, DEVICE_NAME, c.device_id, CLIENT_VERSION);
}

http_get :: (c: *Jellyfin_Client, path: string) -> Http_Response {
    return http_request(c, "GET", path, "");
}

http_post :: (c: *Jellyfin_Client, path: string, json_body: string) -> Http_Response {
    return http_request(c, "POST", path, json_body);
}

#scope_file

Write_Buffer :: struct {
    builder: String_Builder;
}

write_callback :: (contents: *u8, count: u64, size: u64, user: *Write_Buffer) -> u64 #c_call {
    total := count * size;
    push_context {
        append(*user.builder, contents, cast(s64) total);
    }
    return total;
}

http_request :: (c: *Jellyfin_Client, method: string, path: string, body: string) -> Http_Response {
    using Curl;

    response: Http_Response;

    handle := curl_easy_init();
    if !handle {
        log_error("curl_easy_init failed");
        return response;
    }
    defer curl_easy_cleanup(handle);

    url := tprint("%/%", c.server_url, trim_left(path, "/"));
    curl_easy_setopt(handle, CURLoption.URL, temp_c_string(url));

    buf: Write_Buffer;
    curl_easy_setopt(handle, CURLoption.WRITEFUNCTION, write_callback);
    curl_easy_setopt(handle, CURLoption.WRITEDATA, *buf);

    headers: *curl_slist;
    auth := build_auth_header(c);
    headers = curl_slist_append(headers, temp_c_string(tprint("Authorization: %", auth)));
    headers = curl_slist_append(headers, temp_c_string("Accept: application/json"));

    if method == "POST" || method == "PUT" {
        headers = curl_slist_append(headers, temp_c_string("Content-Type: application/json"));
        curl_easy_setopt(handle, CURLoption.POSTFIELDS, temp_c_string(body));
        curl_easy_setopt(handle, CURLoption.POSTFIELDSIZE, cast(s64) body.count);
    }

    if method == "GET" {
        // default
    } else if method != "POST" {
        curl_easy_setopt(handle, CURLoption.CUSTOMREQUEST, temp_c_string(method));
    }

    curl_easy_setopt(handle, CURLoption.HTTPHEADER, headers);
    defer curl_slist_free_all(headers);

    err := curl_easy_perform(handle);
    if err != .OK {
        msg := to_string(curl_easy_strerror(err));
        log_error("curl % %: %", method, url, msg);
        return response;
    }

    code: s64;
    curl_easy_getinfo(handle, CURLINFO.RESPONSE_CODE, *code);
    response.status_code = code;
    response.body        = builder_to_string(*buf.builder);
    response.ok          = code >= 200 && code < 300;
    return response;
}
