from __future__ import annotations

import argparse
import logging
import platform
import shutil
import subprocess
import time
from pathlib import Path

is_windows = platform.system() == "Windows"
is_linux = platform.system() == "Linux"
is_macos = platform.system() == "Darwin"
ext = ".exe" if is_windows else ""

logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
logger = logging.getLogger(__name__)
cwd = Path.cwd()

_WINDOWS_GUI_TEMPLATE: str = r"""#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>

#define MAX_PATH_LEN 4096
#define MAX_ERROR_LEN 8192

// Check if Python runtime exists
static int check_python_runtime(const char* runtime_path) {
    return GetFileAttributesA(runtime_path) != INVALID_FILE_ATTRIBUTES;
}

// Build Python command line
static void build_python_command(
    char* cmd,
    const char* exe_dir,
    const char* entry_file,
    int is_debug
) {
    char python_runtime[MAX_PATH_LEN];
    char script_path[MAX_PATH_LEN];

    // Build Python interpreter path
    // GUI non-debug mode uses pythonw.exe (no console), other cases use python.exe
    if (is_debug) {
        // Debug mode: use python.exe to show console output
        snprintf(python_runtime, MAX_PATH_LEN, "%s\\runtime\\python.exe", exe_dir);
    } else {
        // Production GUI mode: use pythonw.exe without creating console window
        snprintf(python_runtime, MAX_PATH_LEN, "%s\\runtime\\pythonw.exe", exe_dir);
    }

    // Build startup script path
    snprintf(script_path, MAX_PATH_LEN, "%s\\%s", exe_dir, entry_file);

    // Build command line (add -u parameter for real-time output capture)
    if (is_debug) {
        // Debug mode: do not redirect output, display output on console
        snprintf(cmd, MAX_PATH_LEN, "\"%s\" -u \"%s\"", python_runtime, script_path);
    } else {
        // Production mode: redirect all output to pipe
        snprintf(cmd, MAX_PATH_LEN, "\"%s\" -u \"%s\" 2>&1", python_runtime, script_path);
    }
}

// Read process output
static void read_process_output(HANDLE hPipe, char* output, int max_len) {
    DWORD bytes_read;
    output[0] = '\0';

    while (ReadFile(hPipe, output + strlen(output), max_len - strlen(output) - 1, &bytes_read, NULL) && bytes_read > 0) {
        output[bytes_read] = '\0';
    }
}

// Show message box (support UTF-8 encoding)
static void show_message_box(const char* title, const char* message) {
    // Convert UTF-8 to UTF-16 using MultiByteToWideChar
    int title_len = MultiByteToWideChar(CP_UTF8, 0, title, -1, NULL, 0);
    int msg_len = MultiByteToWideChar(CP_UTF8, 0, message, -1, NULL, 0);

    if (title_len > 0 && msg_len > 0) {
        wchar_t* wtitle = (wchar_t*)malloc(title_len * sizeof(wchar_t));
        wchar_t* wmsg = (wchar_t*)malloc(msg_len * sizeof(wchar_t));

        if (wtitle && wmsg) {
            MultiByteToWideChar(CP_UTF8, 0, title, -1, wtitle, title_len);
            MultiByteToWideChar(CP_UTF8, 0, message, -1, wmsg, msg_len);
            MessageBoxW(NULL, wmsg, wtitle, MB_ICONERROR | MB_OK);
        }

        if (wtitle) free(wtitle);
        if (wmsg) free(wmsg);
    } else {
        // Conversion failed, use ANSI version
        MessageBoxA(NULL, message, title, MB_ICONERROR | MB_OK);
    }
}

// Windows GUI entry point
int APIENTRY WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow
) {
    char exe_dir[MAX_PATH_LEN];
    char cmd[MAX_PATH_LEN * 2];
    char error_output[MAX_ERROR_LEN] = "";
    STARTUPINFOA si = {0};
    PROCESS_INFORMATION pi = {0};
    SECURITY_ATTRIBUTES sa = {0};
    HANDLE hReadPipe = NULL, hWritePipe = NULL;
    BOOL success;

    // Get executable directory
    GetModuleFileNameA(NULL, exe_dir, MAX_PATH_LEN);
    char* last_slash = strrchr(exe_dir, '\\');
    if (last_slash) {
        *last_slash = '\0';
    }

    // Check Python runtime
    if (!check_python_runtime(exe_dir)) {
        show_message_box(
            "Application Error",
            "Python runtime not found.\n\n"
            "Please ensure the application is installed correctly and the runtime directory exists."
        );
        return 1;
    }

    // Build and execute Python command
    build_python_command(cmd, exe_dir, "${ENTRY_FILE}", ${DEBUG_MODE});

    // Create pipe for capturing output
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;

    if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0)) {
        show_message_box("Error", "Failed to create pipe for error output.");
        return 1;
    }

    // Set startup info
    si.cb = sizeof(si);

    // Set different startup methods based on debug mode
    if (${DEBUG_MODE}) {
        // Debug mode: do not use pipe, inherit console to display output
        // Keep si.dwFlags as 0, do not set STARTF_USESTDHANDLES
    } else {
        // Production GUI mode: use pipe to capture error output
        si.dwFlags = STARTF_USESTDHANDLES;
        si.hStdError = hWritePipe;
        si.hStdOutput = hWritePipe;
    }

    // Create Python process
    // Debug mode: do not use CREATE_NO_WINDOW, let python.exe create console
    // Production GUI mode: use CREATE_NO_WINDOW to ensure no console is created
    DWORD createFlags = ${DEBUG_MODE} ? 0 : CREATE_NO_WINDOW;
    success = CreateProcessA(
        NULL, cmd, NULL, NULL, FALSE,
        createFlags,
        NULL, exe_dir, &si, &pi
    );

    if (!success) {
        DWORD error = GetLastError();
        char error_msg[MAX_ERROR_LEN];
        snprintf(error_msg, MAX_ERROR_LEN,
            "Failed to start Python process.\n\n"
            "Error code: %lu\n"
            "Command: %s",
            error, cmd);
        show_message_box("Application Error", error_msg);
        if (!${DEBUG_MODE}) {
            CloseHandle(hWritePipe);
            CloseHandle(hReadPipe);
        }
        return 1;
    }

    // Read error output (only in non-debug mode)
    if (!${DEBUG_MODE}) {
        CloseHandle(hWritePipe);
        read_process_output(hReadPipe, error_output, MAX_ERROR_LEN);
        CloseHandle(hReadPipe);
    }

    // Wait for process to end
    WaitForSingleObject(pi.hProcess, INFINITE);

    // Get exit code
    DWORD exit_code;
    GetExitCodeProcess(pi.hProcess, &exit_code);

    // Debug output
    fprintf(stderr, "DEBUG: Python process exited with code: %lu\n", exit_code);

    // Cleanup
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    // If process exits abnormally and there is error output, display error message
    if (exit_code != 0 && strlen(error_output) > 0) {
        // Truncate the last error message (up to 2000 characters)
        size_t error_len = strlen(error_output);
        if (error_len > 2000) {
            error_output[2000] = '\0';
        }
        show_message_box("Application Error", error_output);
        return exit_code;
    }

    return exit_code;
}
"""

_WINDOWS_CONSOLE_TEMPLATE: str = r"""#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <locale.h>

#define MAX_PATH_LEN 4096

// Set console encoding to UTF-8
static void setup_encoding() {
    SetConsoleOutputCP(CP_UTF8);
    SetConsoleCP(CP_UTF8);
    setlocale(LC_ALL, ".UTF8");
}

// Check if Python runtime exists
static int check_python_runtime(const char* runtime_path) {
    return GetFileAttributesA(runtime_path) != INVALID_FILE_ATTRIBUTES;
}

// Build Python command line
static void build_python_command(
    char* cmd,
    const char* exe_dir,
    const char* entry_file,
    int debug_mode
) {
    char python_runtime[MAX_PATH_LEN];
    char script_path[MAX_PATH_LEN];

    // Build Python interpreter path
    snprintf(python_runtime, MAX_PATH_LEN, "%s\\runtime\\python.exe", exe_dir);

    // Build startup script path
    snprintf(script_path, MAX_PATH_LEN, "%s\\%s", exe_dir, entry_file);

    // Build command line (add -u parameter for real-time output capture)
    if (debug_mode) {
        snprintf(cmd, MAX_PATH_LEN, "\"%s\" \"%s\"", python_runtime, script_path);
    } else {
        snprintf(cmd, MAX_PATH_LEN, "\"%s\" -u \"%s\" 2>&1", python_runtime, script_path);
    }
}

// Windows Console entry point
int main(int argc, char* argv[]) {
    char exe_dir[MAX_PATH_LEN];
    char cmd[MAX_PATH_LEN * 2];
    STARTUPINFOA si = {0};
    PROCESS_INFORMATION pi = {0};

    // Set encoding to UTF-8
    setup_encoding();

    // Get executable directory
    GetModuleFileNameA(NULL, exe_dir, MAX_PATH_LEN);
    char* last_slash = strrchr(exe_dir, '\\');
    if (last_slash) {
        *last_slash = '\0';
    }

    // Check Python runtime
    char python_runtime_check[MAX_PATH_LEN];
    snprintf(python_runtime_check, MAX_PATH_LEN, "%s\\runtime\\python.exe", exe_dir);
    if (!check_python_runtime(python_runtime_check)) {
        fprintf(stderr, "Error: Python runtime not found at %s\\runtime\\\n", exe_dir);
        fprintf(stderr, "Please ensure the application is installed correctly.\n");
        return 1;
    }

    // Build and execute Python command
    build_python_command(cmd, exe_dir, "${ENTRY_FILE}", ${DEBUG_MODE});

    // Debug output
    if (${DEBUG_MODE}) {
        fprintf(stderr, "DEBUG: Command to execute: %s\n", cmd);
        fprintf(stderr, "DEBUG: exe_dir: %s\n", exe_dir);
    }

    // Set startup info to inherit console
    si.cb = sizeof(si);

    // Ensure standard handles are inherited so output displays on console
    si.dwFlags = STARTF_USESTDHANDLES;
    si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
    si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    si.hStdError = GetStdHandle(STD_ERROR_HANDLE);

    // Create Python process
    if (!CreateProcessA(
        NULL, cmd, NULL, NULL, TRUE,  // TRUE to inherit standard handles
        0, NULL, exe_dir, &si, &pi
    )) {
        DWORD error = GetLastError();
        fprintf(stderr, "Error: Failed to start Python process.\n");
        fprintf(stderr, "Error code: %lu\n", error);
        fprintf(stderr, "Command: %s\n", cmd);
        return 1;
    }

    // Wait for process to end
    WaitForSingleObject(pi.hProcess, INFINITE);

    // Get exit code
    DWORD exit_code;
    GetExitCodeProcess(pi.hProcess, &exit_code);

    // Debug output
    if (${DEBUG_MODE}) {
        fprintf(stderr, "DEBUG: Python process exited with code: %lu\n", exit_code);
    }

    // Cleanup
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    // If process exits abnormally, display prompt
    if (exit_code != 0 && !${DEBUG_MODE}) {
        fprintf(stderr, "\nApplication exited abnormally, error code: %lu\n", exit_code);
        fprintf(stderr, "Please check the error information above for details.\n");
    }

    return exit_code;
}
"""

_UNIX_GUI_TEMPLATE: str = r"""#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define MAX_PATH_LEN 4096
#define MAX_ERROR_LEN 8192

// Build Python command line
static void build_python_command(
    char* cmd,
    const char* exe_dir,
    const char* entry_file
) {
    char script_path[MAX_PATH_LEN];

    // Build startup script path
    snprintf(script_path, MAX_PATH_LEN * 2, "%s/%s", exe_dir, entry_file);

    // Build log file path (record error information)
    char log_file[MAX_PATH_LEN];
    snprintf(log_file, MAX_PATH_LEN, "%s/.error_log", exe_dir);

    // Build command line - use system python3 instead of bundled runtime
    // GUI mode uses nohup and background execution, error output to log
    snprintf(cmd, MAX_PATH_LEN * 2,
        "cd \"%s\" && nohup python3 -u \"%s\" >\"%s\" 2>&1 &",
        exe_dir, script_path, log_file
    );
}

// Unix GUI entry point
int main(int argc, char* argv[]) {
    char exe_dir[MAX_PATH_LEN];
    char cmd[MAX_PATH_LEN * 2];
    char log_file[MAX_PATH_LEN];
    pid_t pid;
    int status;

    // Get executable directory
    if (realpath("/proc/self/exe", exe_dir) == NULL) {
        // If /proc/self/exe is unavailable (like on macOS), use argv[0]
        if (realpath(argv[0], exe_dir) == NULL) {
            fprintf(stderr, "Error: Cannot determine executable directory\n");
            return 1;
        }
    }

    // Remove executable name, keep only directory
    char* last_slash = strrchr(exe_dir, '/');
    if (last_slash) {
        *last_slash = '\0';
    }

    // Build and execute Python command
    build_python_command(cmd, exe_dir, "${ENTRY_FILE}");
    snprintf(log_file, MAX_PATH_LEN, "%s/.error_log", exe_dir);

    // Execute Python process in background
    pid = fork();
    if (pid == 0) {
        // Child process: execute Python command and exit immediately
        int ret = system(cmd);
        _exit(WEXITSTATUS(ret));
    } else if (pid < 0) {
        fprintf(stderr, "Error: Failed to fork process\n");
        return 1;
    }

    // Wait for a short time to check if process failed to start
    sleep(1);
    if (waitpid(pid, &status, WNOHANG) == pid) {
        // Process has already exited, read error log
        FILE* fp = fopen(log_file, "r");
        if (fp) {
            char error_msg[MAX_ERROR_LEN];
            if (fgets(error_msg, sizeof(error_msg), fp)) {
                fprintf(stderr, "Application failed to start:\n%s\n", error_msg);
            }
            fclose(fp);
        } else {
            fprintf(stderr, "Application failed to start (exit code: %d)\n", WEXITSTATUS(status));
        }
        return WEXITSTATUS(status);
    }

    // Parent process exits immediately
    return 0;
}
"""

_UNIX_CONSOLE_TEMPLATE: str = r"""#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define MAX_PATH_LEN 4096

// Build Python command line
static void build_python_command(
    char* cmd,
    const char* exe_dir,
    const char* entry_file
) {
    char script_path[MAX_PATH_LEN];

    // Build startup script path
    snprintf(script_path, MAX_PATH_LEN, "%s/%s", exe_dir, entry_file);

    // Build command line - use system python3 instead of bundled runtime
    // add -u parameter for real-time output capture
    snprintf(cmd, MAX_PATH_LEN * 2,
        "cd \"%s\" && python3 -u \"%s\"",
        exe_dir, script_path
    );
}

// Unix Console entry point
int main(int argc, char* argv[]) {
    char exe_dir[MAX_PATH_LEN];
    char cmd[MAX_PATH_LEN * 2];
    pid_t pid;
    int status;

    // Get executable directory
    if (realpath("/proc/self/exe", exe_dir) == NULL) {
        fprintf(stderr, "Error: Cannot determine executable directory\n");
        return 1;
    }

    // Remove executable name, keep only directory
    char* last_slash = strrchr(exe_dir, '/');
    if (last_slash) {
        *last_slash = '\0';
    }

    // Build and execute Python command
    build_python_command(cmd, exe_dir, "${ENTRY_FILE}");

    // Fork and execute Python process
    pid = fork();
    if (pid == 0) {
        // Child process: execute Python command
        execl("/bin/sh", "sh", "-c", cmd, NULL);
        fprintf(stderr, "Error: Failed to execute Python process\n");
        _exit(127); // If execl fails
    } else if (pid < 0) {
        fprintf(stderr, "Error: Failed to fork process\n");
        return 1;
    }

    // Parent process: wait for child process to end
    waitpid(pid, &status, 0);

    int exit_code = WEXITSTATUS(status);
    if (exit_code != 0) {
        fprintf(stderr, "\nApplication exited abnormally, error code: %d\n", exit_code);
        fprintf(stderr, "Please check the error information above for details.\n");
    }

    return exit_code;
}
"""

_MACOS_GUI_TEMPLATE: str = r"""#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <CoreFoundation/CoreFoundation.h>
#include <ApplicationServices/ApplicationServices.h>

#define MAX_PATH_LEN 4096
#define MAX_ERROR_LEN 8192

// Check if Python interpreter exists
static int check_python_runtime(const char* runtime_path) {
    struct stat st;
    return stat(runtime_path, &st) == 0 && (st.st_mode & S_IXUSR);
}

// Get executable directory (macOS specific)
static void get_exe_dir_macos(char* exe_dir, size_t size) {
    CFBundleRef bundle = CFBundleGetMainBundle();
    if (bundle) {
        CFURLRef url = CFBundleCopyBundleURL(bundle);
        if (url) {
            CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
            if (path) {
                CFStringGetCString(path, exe_dir, size, kCFStringEncodingUTF8);
                CFRelease(path);
            }
            CFRelease(url);
        }
    } else {
        // If not in bundle, use argv[0]
        realpath("./", exe_dir);
    }

    // Remove Contents/MacOS suffix of bundle
    char* macos_path = strstr(exe_dir, ".app/Contents/MacOS");
    if (macos_path) {
        *macos_path = '\0';
    }
    }

    // Display macOS error dialog
    static void show_error_dialog(const char* message) {
    CFStringRef cf_message = CFStringCreateWithCString(
        NULL, message, kCFStringEncodingUTF8
    );
    CFOptionFlags response;
    CFUserNotificationDisplayAlert(
        0,
        kCFUserNotificationStopAlertLevel,
        NULL, NULL, NULL,
        CFSTR("Application Error"),
        cf_message,
        CFSTR("OK"), NULL, NULL, &response
    );
    CFRelease(cf_message);
}

// Build Python command line
static void build_python_command(
    char* cmd,
    const char* exe_dir,
    const char* entry_file
) {
    char python_runtime[MAX_PATH_LEN];
    char script_path[MAX_PATH_LEN];
    char log_file[MAX_PATH_LEN];

    // macOS Python path
    snprintf(python_runtime, MAX_PATH_LEN, "%s/runtime/bin/python3", exe_dir);
    snprintf(script_path, MAX_PATH_LEN, "%s/%s", exe_dir, entry_file);

    // Build log file path
    snprintf(log_file, MAX_PATH_LEN, "%s/.error_log", exe_dir);

    // Build command line (GUI mode uses nohup and background execution, error output to log)
    snprintf(cmd, MAX_PATH_LEN * 2,
        "cd \"%s\" && nohup \"%s\" -u \"%s\" >\"%s\" 2>&1 &",
        exe_dir, python_runtime, script_path, log_file
    );
}

// macOS GUI entry point
int main(int argc, char* argv[]) {
    char exe_dir[MAX_PATH_LEN];
    char cmd[MAX_PATH_LEN * 2];
    char log_file[MAX_PATH_LEN];
    pid_t pid;
    int status;

    // Get executable directory
    get_exe_dir_macos(exe_dir, MAX_PATH_LEN);

    // Check Python runtime
    char python_runtime[MAX_PATH_LEN];
    snprintf(python_runtime, MAX_PATH_LEN, "%s/runtime/bin/python3", exe_dir);
    if (!check_python_runtime(python_runtime)) {
        // macOS GUI app needs to use dialog to display error
        char error_msg[MAX_PATH_LEN];
        snprintf(error_msg, MAX_PATH_LEN,
            "Python runtime not found.\n\n"
            "Please ensure the application is installed correctly and the runtime directory is at:\n%s/runtime/bin/",
            exe_dir);
        show_error_dialog(error_msg);
        return 1;
    }

    // Build and execute Python command
    build_python_command(cmd, exe_dir, "${ENTRY_FILE}");
    snprintf(log_file, MAX_PATH_LEN, "%s/.error_log", exe_dir);

    // Execute Python process in background
    pid = fork();
    if (pid == 0) {
        // Child process
        int ret = system(cmd);
        _exit(WEXITSTATUS(ret));
    } else if (pid < 0) {
        char error_msg[MAX_PATH_LEN];
        snprintf(error_msg, MAX_PATH_LEN, "Failed to create process.\nError: %s", strerror(errno));
        show_error_dialog(error_msg);
        return 1;
    }

    // Wait for a short time to check if process failed to start
    sleep(1);
    if (waitpid(pid, &status, WNOHANG) == pid) {
        // Process has already exited, read error log
        FILE* fp = fopen(log_file, "r");
        if (fp) {
            char error_msg[MAX_ERROR_LEN];
            if (fgets(error_msg, sizeof(error_msg), fp)) {
                // Remove newline
                char* newline = strchr(error_msg, '\n');
                if (newline) *newline = '\0';
                char full_error[MAX_PATH_LEN + MAX_ERROR_LEN];
                snprintf(full_error, sizeof(full_error),
                    "Application failed to start.\n\nError:\n%s", error_msg);
                show_error_dialog(full_error);
            }
            fclose(fp);
        } else {
            char error_msg[MAX_PATH_LEN];
            snprintf(error_msg, MAX_PATH_LEN,
                "Application failed to start.\n\nExit code: %d",
                WEXITSTATUS(status));
            show_error_dialog(error_msg);
        }
        return WEXITSTATUS(status);
    }

    return 0;
}
"""

_MACOS_CONSOLE_TEMPLATE: str = r"""#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define MAX_PATH_LEN 4096

// Check if Python interpreter exists
static int check_python_runtime(const char* runtime_path) {
    struct stat st;
    return stat(runtime_path, &st) == 0 && (st.st_mode & S_IXUSR);
}

// Get executable directory (macOS specific)
static void get_exe_dir_macos(char* exe_dir, size_t size) {
    CFBundleRef bundle = CFBundleGetMainBundle();
    if (bundle) {
        CFURLRef url = CFBundleCopyBundleURL(bundle);
        if (url) {
            CFStringRef path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
            if (path) {
                CFStringGetCString(path, exe_dir, size, kCFStringEncodingUTF8);
                CFRelease(path);
            }
            CFRelease(url);
        }
    } else {
        realpath("./", exe_dir);
    }
}

// Build Python command line
static void build_python_command(
    char* cmd,
    const char* exe_dir,
    const char* entry_file
) {
    char python_runtime[MAX_PATH_LEN];
    char script_path[MAX_PATH_LEN];

    snprintf(python_runtime, MAX_PATH_LEN, "%s/runtime/bin/python3", exe_dir);
    snprintf(script_path, MAX_PATH_LEN, "%s/%s", exe_dir, entry_file);

    // Build command line (add -u parameter for real-time output capture)
    snprintf(cmd, MAX_PATH_LEN,
        "cd \"%s\" && \"%s\" -u \"%s\"",
        exe_dir, python_runtime, script_path
    );
}

// macOS Console entry point
int main(int argc, char* argv[]) {
    char exe_dir[MAX_PATH_LEN];
    char cmd[MAX_PATH_LEN * 2];
    pid_t pid;
    int status;

    // Get executable directory
    get_exe_dir_macos(exe_dir, MAX_PATH_LEN);

    // Check Python runtime
    char python_runtime[MAX_PATH_LEN];
    snprintf(python_runtime, MAX_PATH_LEN, "%s/runtime/bin/python3", exe_dir);
    if (!check_python_runtime(python_runtime)) {
        fprintf(stderr, "Error: Python runtime not found at %s/runtime/bin/\n", exe_dir);
        fprintf(stderr, "Please ensure the application is installed correctly.\n");
        return 1;
    }

    // Build and execute Python command
    build_python_command(cmd, exe_dir, "${ENTRY_FILE}");

    // Fork and execute Python process
    pid = fork();
    if (pid == 0) {
        execl("/bin/sh", "sh", "-c", cmd, NULL);
        fprintf(stderr, "Error: Failed to execute Python process\n");
        _exit(127);
    } else if (pid < 0) {
        fprintf(stderr, "Error: Failed to create process: %s\n", strerror(errno));
        return 1;
    }

    waitpid(pid, &status, 0);

    int exit_code = WEXITSTATUS(status);
    if (exit_code != 0) {
        fprintf(stderr, "\nApplication exited abnormally, error code: %d\n", exit_code);
        fprintf(stderr, "Please check the error information above for details.\n");
    }

    return exit_code;
}
"""

_COMPILER_OPTIONS: dict[str, list[str]] = {
    "gcc": ["-std=c99", "-Wall", "-Wextra", "-pedantic", "-O2", "-D_GNU_SOURCE"],
    "clang": ["-std=c99", "-Wall", "-Wextra", "-pedantic", "-O2", "-D_GNU_SOURCE"],
    "cl": ["/std:c99", "/Wall", "/Wextra", "/pedantic", "/O2", "/D_GNU_SOURCE"],
}


def find_compiler() -> str | None:
    """Find the path to the specified compiler."""
    compilers = _COMPILER_OPTIONS.keys()

    for compiler in compilers:
        if shutil.which(compiler) is not None:
            return compiler

    logger.error("No compiler found")
    return None


def get_compiler_args(
    compiler: str,
) -> list[str]:
    """Get the arguments for the specified compiler."""
    compiler_name = Path(compiler).stem if "\\" in compiler or "/" in compiler else compiler

    if compiler_name in ("gcc", "clang"):
        return ["-std=c99", "-Wall", "-pedantic", "-Werror"]
    elif compiler_name == "cl":
        return ["/std:c99", "/Wall", "/WX", "/Werror"]
    else:
        return []


def select_template(
    loader_type: str,
    is_debug: bool,
) -> str:
    """Select the appropriate C code template based on platform and type.

    In debug mode, always use console template to ensure output is visible.
    """
    # In debug mode, always use console template for visibility
    if is_debug:
        if is_windows:
            return _WINDOWS_CONSOLE_TEMPLATE
        elif is_macos:
            return _MACOS_CONSOLE_TEMPLATE
        else:
            return _UNIX_CONSOLE_TEMPLATE

    # In non-debug mode, use the requested template type
    if is_windows:
        if loader_type == "gui":
            return _WINDOWS_GUI_TEMPLATE
        else:
            return _WINDOWS_CONSOLE_TEMPLATE
    elif is_macos:
        if loader_type == "gui":
            return _MACOS_GUI_TEMPLATE
        else:
            return _MACOS_CONSOLE_TEMPLATE
    else:  # Linux and other Unix-like systems
        if loader_type == "gui":
            return _UNIX_GUI_TEMPLATE
        else:
            return _UNIX_CONSOLE_TEMPLATE


def generate_c_source(
    template: str,
    entry_file: str,
    is_debug: bool,
) -> str:
    """Generate C source code by replacing placeholders in template."""
    # Replace placeholders
    c_code = template.replace("${ENTRY_FILE}", entry_file)
    c_code = c_code.replace("${DEBUG_MODE}", "1" if is_debug else "0")
    return c_code


def compile_c_source(
    c_source_path: str | Path,
    output_filepath: Path,
    compiler: str | None = None,
) -> bool:
    """Compile C source code to executable file."""
    # Find compiler if not specified
    if compiler is None:
        compiler = find_compiler()
        if compiler is None:
            logger.error("Error: No suitable C compiler found (gcc/clang/cl required)")
            return False

    logger.debug(f"Using compiler: {compiler}")

    # Get compiler arguments
    compiler_args = get_compiler_args(compiler)

    # Prepare paths using pathlib
    c_source_path = Path(c_source_path)

    output_filepath.parent.mkdir(parents=True, exist_ok=True)
    output_filepath = output_filepath.with_suffix(ext)

    # Build compile command
    compiler_name = Path(compiler).name if "\\" in compiler or "/" in compiler else compiler
    if compiler_name.lower() == "cl" or compiler_name.lower() == "cl.exe":
        # MSVC compiler
        cmd = [
            compiler,
            *compiler_args,
            str(c_source_path),
            f"/Fe:{output_filepath}",
            "/link",
            "/SUBSYSTEM:WINDOWS" if "gui" in str(c_source_path).lower() else "/SUBSYSTEM:CONSOLE",
        ]
    else:
        # GCC/Clang compiler
        subsystem_flag = []
        # For Windows GUI, add -mwindows flag with gcc/clang
        if is_windows and "gui" in str(c_source_path).lower():
            subsystem_flag = ["-mwindows"]
        cmd = [compiler, *compiler_args, *subsystem_flag, "-o", str(output_filepath), str(c_source_path)]

    logger.debug(f"Compiling with command: {' '.join(cmd)}")
    try:
        result = subprocess.run(cmd, capture_output=True, text=True)
        if result.returncode != 0:
            logger.debug(f"Compilation failed with return code {result.returncode}")
            logger.debug(f"STDOUT: {result.stdout}")
            logger.debug(f"STDERR: {result.stderr}")
            return False
        logger.info(f"Successfully compiled to: {output_filepath}")
        return True
    except FileNotFoundError:
        logger.error(f"Error: Compiler '{compiler}' not found")
        return False
    except Exception as e:
        logger.error(f"Error during compilation: {e}")
        return False


def main():
    parser = argparse.ArgumentParser(description="Generate a Python loader executable for current platform")
    parser.add_argument("--debug", "-d", action="store_true", help="Enable debug mode")
    parser.add_argument(
        "-t", "--type", choices=["gui", "console"], default="console", help="Loader type (default: console)"
    )
    parser.add_argument("-o", "--output", default="", help="Output executable path (default: pyloader or pyloader.exe)")
    parser.add_argument(
        "-e", "--entry-file", default="main.py", help="Entry Python file path to execute (default: main.py)"
    )
    parser.add_argument(
        "--compiler", help="Specify compiler to use. Examples: gcc, clang, cl, or full path like C:\\vc\\bin\\cl.exe"
    )
    parser.add_argument("--run", "-r", action="store_true", help="Run the generated executable after compilation")

    args = parser.parse_args()

    if args.debug:
        logger.setLevel(logging.DEBUG)

    # Determine default output file name based on platform and type
    output_filepath = cwd / f"pyloader{ext}" if not args.output else Path(args.output)

    # Select appropriate template (in debug mode, always use console)
    template = select_template(args.type, args.debug)

    # Generate C source code
    c_code = generate_c_source(template, args.entry_file, args.debug)

    # Write C source code to file
    # File name should reflect actual template type, not just args.type
    # (debug mode forces console template)
    if args.debug:
        c_source_file = "pyloader_debug_console.c"
    elif args.type == "console":
        c_source_file = "pyloader_console.c"
    else:  # gui type in non-debug mode
        c_source_file = "pyloader_gui.c"

    c_source_path = Path(c_source_file)
    c_source_path.write_text(c_code, encoding="utf-8")
    logger.info(f"Generated C source code: {c_source_path}")

    t0 = time.perf_counter()

    # Compile to executable
    if compile_c_source(c_source_file, output_filepath, args.compiler):
        logger.info("Loader executable generated successfully!")
        logger.debug(f"Platform: {platform.system()}")
        logger.debug(f"Type: {args.type}")
        logger.debug(f"Debug mode: {args.debug}")
        logger.debug(f"Python entry file: {args.entry_file}")
        logger.debug(f"Output: {output_filepath}")

        # Run the executable if --run flag is set
        if args.run:
            logger.debug("\nRunning the executable...")
            import subprocess

            try:
                subprocess.run([str(Path(args.output))], check=True)
            except subprocess.CalledProcessError as e:
                logger.error(f"\nExecutable exited with code: {e.returncode}")
                return e.returncode
            except FileNotFoundError:
                logger.error(f"\nError: Executable not found at {args.output}")
                return 1

        logger.info(f"Compilation time: {time.perf_counter() - t0:.4f} seconds")
        return 0
    else:
        logger.error("\nFailed to compile loader executable")
        return 1


if __name__ == "__main__":
    import sys

    sys.exit(main())
