#import "Basic";
#import "Curl";
#import "File";
#import "Thread";
#import "Windows";
#import "String";


Updater_Thread_Data :: struct {
    hwnd: HWND;
    update_interval_seconds: s32 = 3600; // 1 hour
    stop_event: HANDLE;
}

write_callback :: (ptr: *u8, size: u64, nmemb: u64, userdata: *void) -> u64 #c_call {
    builder := cast(*String_Builder) userdata;
    push_context {
        append(builder, ptr, xx nmemb);
    }
    return nmemb;
}

download_url :: (url: string) -> string, bool, string {
    curl := curl_easy_init();
    if !curl return "", false, "Failed to initialize Curl";
    defer curl_easy_cleanup(curl);

    curl_easy_setopt(curl, .URL, temp_c_string(url));
    curl_easy_setopt(curl, .FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, .TIMEOUT, 30);

    // Disable SSL verification to prevent issues with missing CA cert bundles on Windows
    curl_easy_setopt(curl, .SSL_VERIFYPEER, 0);
    curl_easy_setopt(curl, .SSL_VERIFYHOST, 0);

    builder: String_Builder;
    defer free_buffers(*builder);

    curl_easy_setopt(curl, .WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, .WRITEDATA, *builder);

    curl_code := curl_easy_perform(curl);
    if curl_code != .OK {
        err_msg := tprint("Curl perform failed: %", to_string(curl_easy_strerror(curl_code)));
        return "", false, err_msg;
    }

    response_code: int;
    curl_easy_getinfo(curl, .RESPONSE_CODE, *response_code);
    if response_code != 200 {
        err_msg := tprint("HTTP request failed with status code %", response_code);
        return "", false, err_msg;
    }

    return builder_to_string(*builder), true, "";
}

perform_update :: () -> changed: bool, success: bool, error_msg: string {
    url_data, ok := read_entire_file("url.txt");
    if !ok return false, false, "Could not read url.txt. Please configure the Config URL first.";
    defer free(url_data);

    url := trim(url_data);
    if !url return false, false, "url.txt is empty. Please configure the Config URL first.";

    downloaded, success, err_msg := download_url(url);
    if !success return false, false, err_msg;
    defer free(downloaded);

    modified := modify_config_inbounds(downloaded);
    defer free(modified);

    config_data, read_ok := read_entire_file("config.json");
    defer if read_ok free(config_data);

    if read_ok && config_data == modified {
        return false, true, "";
    }

    write_ok := write_entire_file("config.json", modified);
    if !write_ok {
        return false, false, "Failed to write downloaded content to config.json";
    }

    return true, true, "";
}

updater_thread_proc :: (thread: *Thread) -> s64 {
    data := cast(*Updater_Thread_Data) thread.data;
    if !data return 1;

    while true {
        // Wait on the stop event for the specified interval.
        // If the stop event is signaled, wake up immediately and terminate.
        status := WaitForSingleObject(data.stop_event, cast(DWORD) (data.update_interval_seconds * 1000));
        if status == WAIT_OBJECT_0 {
            break;
        }

        changed, success, err_msg := perform_update();
        if success && changed {
            if data.hwnd {
                PostMessageW(data.hwnd, WM_RESTART_SINGBOX, 0, 0);
            }
        }
    }

    return 0;
}

modify_config_inbounds :: (json_content: string) -> string {
    // Find "inbounds" key
    idx := find_index_from_left(json_content, "\"inbounds\"");
    if idx == -1 {
        first_brace := find_index_from_left(json_content, #char "{");
        if first_brace == -1 return copy_string(json_content);

        builder: String_Builder;
        append(*builder, slice(json_content, 0, first_brace + 1));
        append(*builder, "\n  \"inbounds\": [\n    {\n      \"type\": \"mixed\",\n      \"listen\": \"127.0.0.1\",\n      \"listen_port\": 20122\n    }\n  ],");
        append(*builder, slice(json_content, first_brace + 1, json_content.count - (first_brace + 1)));
        return builder_to_string(*builder);
    }

    // Find the opening bracket '[' after "inbounds"
    search_start := idx + 10;
    open_bracket_idx := -1;
    for i: search_start..json_content.count-1 {
        if json_content[i] == #char "[" {
            open_bracket_idx = i;
            break;
        } else if json_content[i] == #char ":" || is_space(json_content[i]) {
            continue;
        } else {
            break;
        }
    }

    if open_bracket_idx == -1 return copy_string(json_content);

    // Find the matching closing bracket ']'
    close_bracket_idx := -1;
    bracket_depth := 0;
    for i: open_bracket_idx..json_content.count-1 {
        if json_content[i] == #char "[" {
            bracket_depth += 1;
        } else if json_content[i] == #char "]" {
            bracket_depth -= 1;
            if bracket_depth == 0 {
                close_bracket_idx = i;
                break;
            }
        }
    }

    if close_bracket_idx == -1 return copy_string(json_content);

    builder: String_Builder;
    append(*builder, slice(json_content, 0, open_bracket_idx));
    append(*builder, "[\n    {\n      \"type\": \"mixed\",\n      \"listen\": \"127.0.0.1\",\n      \"listen_port\": 20122\n    }\n  ]");
    append(*builder, slice(json_content, close_bracket_idx + 1, json_content.count - (close_bracket_idx + 1)));

    return builder_to_string(*builder);
}
