#include "managers/application_manager.h"

#include <izo/Environment.hpp>
#include <izo/Paths.hpp>
#include <izo/Process.hpp>

#include <algorithm>

namespace
{
    std::filesystem::path environmentPath(const char *name)
    {
        const auto value = izo::GetEnvVar(name);
        return value && !value->empty() ? std::filesystem::path(*value) : std::filesystem::path{};
    }

    std::filesystem::path under(const std::filesystem::path &root, const char *relative)
    {
        return root.empty() ? std::filesystem::path{} : root / relative;
    }

    std::filesystem::path firstExisting(std::initializer_list<std::filesystem::path> candidates)
    {
        std::error_code error;

        for (const auto &candidate : candidates)
        {
            if (!candidate.empty() && std::filesystem::is_regular_file(candidate, error))
                return candidate;
        }

        return {};
    }

    std::filesystem::path findExecutable(const std::filesystem::path &root, const char *filename)
    {
        std::error_code error;

        if (root.empty() || !std::filesystem::is_directory(root, error))
            return {};

        for (std::filesystem::recursive_directory_iterator iterator(
                 root, std::filesystem::directory_options::skip_permission_denied, error),
             end;
             iterator != end && !error; iterator.increment(error))
        {
            if (iterator->is_regular_file(error) && iterator->path().filename() == filename)
                return iterator->path();
        }

        return {};
    }
}

ApplicationManager::ApplicationManager()
{
    const auto local = izo::GetKnownPath(izo::KnownPath::LocalAppData);
    const auto roaming = izo::GetKnownPath(izo::KnownPath::AppData);

    const auto program_files = environmentPath("ProgramFiles");
    const auto program_files_x86 = environmentPath("ProgramFiles(x86)");
    const auto program_data = environmentPath("ProgramData");
    const auto user_profile = environmentPath("USERPROFILE");
    const auto windows = environmentPath("WINDIR");
    const auto chocolatey = environmentPath("ChocolateyInstall");

    auto add = [this](ExternalApplicationId id, const char *name, std::filesystem::path executable,
                      std::vector<std::string> arguments = {})
    {
        const bool available = !executable.empty();
        targets_.push_back({{id, name, available, std::move(executable)}, std::move(arguments)});
        applications_.push_back(targets_.back().info);
    };

    // -------------------------------------------------------------------------
    // VS Code-style editors
    // -------------------------------------------------------------------------

    add(ExternalApplicationId::visual_studio_code, "VS Code", firstExisting({
        under(local, "Programs/Microsoft VS Code/Code.exe"),
        under(program_files, "Microsoft VS Code/Code.exe"),
        under(program_files_x86, "Microsoft VS Code/Code.exe"),
        under(user_profile, "scoop/apps/vscode/current/Code.exe"),
        under(chocolatey, "lib/vscode/tools/Code.exe"),
    }));

    add(ExternalApplicationId::visual_studio_code_insiders, "VS Code Insiders", firstExisting({
        under(local, "Programs/Microsoft VS Code Insiders/Code - Insiders.exe"),
        under(program_files, "Microsoft VS Code Insiders/Code - Insiders.exe"),
        under(user_profile, "scoop/apps/vscode-insiders/current/Code - Insiders.exe"),
    }));

    add(ExternalApplicationId::vscodium, "VSCodium", firstExisting({
        under(local, "Programs/VSCodium/VSCodium.exe"),
        under(program_files, "VSCodium/VSCodium.exe"),
        under(program_files_x86, "VSCodium/VSCodium.exe"),
        under(user_profile, "scoop/apps/vscodium/current/VSCodium.exe"),
        under(chocolatey, "lib/vscodium/tools/VSCodium.exe"),
    }));

    add(ExternalApplicationId::cursor, "Cursor", firstExisting({
        under(local, "Programs/cursor/Cursor.exe"),
        under(local, "Programs/Cursor/Cursor.exe"),
        under(program_files, "Cursor/Cursor.exe"),
        under(user_profile, "scoop/apps/cursor/current/Cursor.exe"),
    }));

    add(ExternalApplicationId::windsurf, "Windsurf", firstExisting({
        under(local, "Programs/Windsurf/Windsurf.exe"),
        under(program_files, "Windsurf/Windsurf.exe"),
        under(user_profile, "scoop/apps/windsurf/current/Windsurf.exe"),
    }));

    add(ExternalApplicationId::trae, "Trae", firstExisting({
        under(local, "Programs/Trae/Trae.exe"),
        under(program_files, "Trae/Trae.exe"),
    }));

    add(ExternalApplicationId::zed, "Zed", firstExisting({
        under(local, "Programs/Zed/Zed.exe"),
        under(program_files, "Zed/Zed.exe"),
        under(user_profile, "scoop/apps/zed/current/Zed.exe"),
    }));

    add(ExternalApplicationId::antigravity, "Antigravity", firstExisting({
        under(local, "Programs/Antigravity/Antigravity.exe"),
        under(local, "Antigravity/Antigravity.exe"),
        under(program_files, "Antigravity/Antigravity.exe"),
    }));

    // -------------------------------------------------------------------------
    // Microsoft IDEs
    // -------------------------------------------------------------------------

    add(ExternalApplicationId::visual_studio, "Visual Studio", firstExisting({
        under(program_files, "Microsoft Visual Studio/2022/Community/Common7/IDE/devenv.exe"),
        under(program_files, "Microsoft Visual Studio/2022/Professional/Common7/IDE/devenv.exe"),
        under(program_files, "Microsoft Visual Studio/2022/Enterprise/Common7/IDE/devenv.exe"),
        under(program_files, "Microsoft Visual Studio/2022/Preview/Common7/IDE/devenv.exe"),

        under(program_files_x86, "Microsoft Visual Studio/2019/Community/Common7/IDE/devenv.exe"),
        under(program_files_x86, "Microsoft Visual Studio/2019/Professional/Common7/IDE/devenv.exe"),
        under(program_files_x86, "Microsoft Visual Studio/2019/Enterprise/Common7/IDE/devenv.exe"),
        under(program_files_x86, "Microsoft Visual Studio/2019/Preview/Common7/IDE/devenv.exe"),
    }));

    // -------------------------------------------------------------------------
    // GitHub / Windows tools
    // -------------------------------------------------------------------------

    std::filesystem::path github_desktop = firstExisting({
        under(local, "GitHubDesktop/GitHubDesktop.exe"),
        under(local, "GitHub Desktop/GitHubDesktop.exe"),
    });

    if (github_desktop.empty())
        github_desktop = findExecutable(under(local, "GitHubDesktop"), "GitHubDesktop.exe");

    add(ExternalApplicationId::github_desktop, "GitHub Desktop", std::move(github_desktop));

    add(ExternalApplicationId::file_explorer, "File Explorer", firstExisting({
        under(windows, "explorer.exe"),
    }));

    add(ExternalApplicationId::terminal, "Terminal", firstExisting({
        under(local, "Microsoft/WindowsApps/wt.exe"),
        under(windows, "System32/wt.exe"),
    }), {"-d"});

    add(ExternalApplicationId::git_bash, "Git Bash", firstExisting({
        under(program_files, "Git/git-bash.exe"),
        under(program_files_x86, "Git/git-bash.exe"),
        under(user_profile, "scoop/apps/git/current/git-bash.exe"),
    }), {"--cd="});

    add(ExternalApplicationId::wsl, "WSL", firstExisting({
        under(windows, "System32/wsl.exe"),
    }), {"--cd"});

    // -------------------------------------------------------------------------
    // Android
    // -------------------------------------------------------------------------

    add(ExternalApplicationId::android_studio, "Android Studio", firstExisting({
        under(program_files, "Android/Android Studio/bin/studio64.exe"),
        under(local, "Programs/Android Studio/bin/studio64.exe"),
        under(user_profile, "scoop/apps/android-studio/current/bin/studio64.exe"),
    }));

    // -------------------------------------------------------------------------
    // JetBrains IDEs
    // -------------------------------------------------------------------------

    std::filesystem::path idea = firstExisting({
        under(program_files, "JetBrains/IntelliJ IDEA 2026.1/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA 2025.3/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA 2025.2/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA 2025.1/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA 2024.3/bin/idea64.exe"),

        under(program_files, "JetBrains/IntelliJ IDEA Community Edition 2026.1/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA Community Edition 2025.3/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA Community Edition 2025.2/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA Community Edition 2025.1/bin/idea64.exe"),
        under(program_files, "JetBrains/IntelliJ IDEA Community Edition 2024.3/bin/idea64.exe"),

        under(local, "Programs/IntelliJ IDEA Ultimate/bin/idea64.exe"),
        under(local, "Programs/IntelliJ IDEA Community/bin/idea64.exe"),
    });

    if (idea.empty())
        idea = findExecutable(under(local, "JetBrains/Toolbox/apps"), "idea64.exe");

    add(ExternalApplicationId::intellij_idea, "IntelliJ IDEA", std::move(idea));

    std::filesystem::path clion = firstExisting({
        under(program_files, "JetBrains/CLion 2026.1/bin/clion64.exe"),
        under(program_files, "JetBrains/CLion 2025.3/bin/clion64.exe"),
        under(program_files, "JetBrains/CLion 2025.2/bin/clion64.exe"),
        under(program_files, "JetBrains/CLion 2025.1/bin/clion64.exe"),
        under(program_files, "JetBrains/CLion 2024.3/bin/clion64.exe"),
        under(local, "Programs/CLion/bin/clion64.exe"),
    });

    if (clion.empty())
        clion = findExecutable(under(local, "JetBrains/Toolbox/apps"), "clion64.exe");

    add(ExternalApplicationId::clion, "CLion", std::move(clion));

    std::filesystem::path rider = firstExisting({
        under(program_files, "JetBrains/JetBrains Rider 2026.1/bin/rider64.exe"),
        under(program_files, "JetBrains/JetBrains Rider 2025.3/bin/rider64.exe"),
        under(program_files, "JetBrains/JetBrains Rider 2025.2/bin/rider64.exe"),
        under(program_files, "JetBrains/JetBrains Rider 2025.1/bin/rider64.exe"),
        under(program_files, "JetBrains/JetBrains Rider 2024.3/bin/rider64.exe"),
        under(local, "Programs/Rider/bin/rider64.exe"),
    });

    if (rider.empty())
        rider = findExecutable(under(local, "JetBrains/Toolbox/apps"), "rider64.exe");

    add(ExternalApplicationId::rider, "Rider", std::move(rider));

    std::filesystem::path pycharm = firstExisting({
        under(program_files, "JetBrains/PyCharm 2026.1/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm 2025.3/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm 2025.2/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm 2025.1/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm 2024.3/bin/pycharm64.exe"),

        under(program_files, "JetBrains/PyCharm Community Edition 2026.1/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm Community Edition 2025.3/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm Community Edition 2025.2/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm Community Edition 2025.1/bin/pycharm64.exe"),
        under(program_files, "JetBrains/PyCharm Community Edition 2024.3/bin/pycharm64.exe"),

        under(local, "Programs/PyCharm Professional/bin/pycharm64.exe"),
        under(local, "Programs/PyCharm Community/bin/pycharm64.exe"),
    });

    if (pycharm.empty())
        pycharm = findExecutable(under(local, "JetBrains/Toolbox/apps"), "pycharm64.exe");

    add(ExternalApplicationId::pycharm, "PyCharm", std::move(pycharm));

    std::filesystem::path webstorm = firstExisting({
        under(program_files, "JetBrains/WebStorm 2026.1/bin/webstorm64.exe"),
        under(program_files, "JetBrains/WebStorm 2025.3/bin/webstorm64.exe"),
        under(program_files, "JetBrains/WebStorm 2025.2/bin/webstorm64.exe"),
        under(program_files, "JetBrains/WebStorm 2025.1/bin/webstorm64.exe"),
        under(program_files, "JetBrains/WebStorm 2024.3/bin/webstorm64.exe"),
        under(local, "Programs/WebStorm/bin/webstorm64.exe"),
    });

    if (webstorm.empty())
        webstorm = findExecutable(under(local, "JetBrains/Toolbox/apps"), "webstorm64.exe");

    add(ExternalApplicationId::webstorm, "WebStorm", std::move(webstorm));

    std::filesystem::path phpstorm = firstExisting({
        under(program_files, "JetBrains/PhpStorm 2026.1/bin/phpstorm64.exe"),
        under(program_files, "JetBrains/PhpStorm 2025.3/bin/phpstorm64.exe"),
        under(program_files, "JetBrains/PhpStorm 2025.2/bin/phpstorm64.exe"),
        under(program_files, "JetBrains/PhpStorm 2025.1/bin/phpstorm64.exe"),
        under(program_files, "JetBrains/PhpStorm 2024.3/bin/phpstorm64.exe"),
        under(local, "Programs/PhpStorm/bin/phpstorm64.exe"),
    });

    if (phpstorm.empty())
        phpstorm = findExecutable(under(local, "JetBrains/Toolbox/apps"), "phpstorm64.exe");

    add(ExternalApplicationId::phpstorm, "PhpStorm", std::move(phpstorm));

    std::filesystem::path rubymine = firstExisting({
        under(program_files, "JetBrains/RubyMine 2026.1/bin/rubymine64.exe"),
        under(program_files, "JetBrains/RubyMine 2025.3/bin/rubymine64.exe"),
        under(program_files, "JetBrains/RubyMine 2025.2/bin/rubymine64.exe"),
        under(program_files, "JetBrains/RubyMine 2025.1/bin/rubymine64.exe"),
        under(program_files, "JetBrains/RubyMine 2024.3/bin/rubymine64.exe"),
        under(local, "Programs/RubyMine/bin/rubymine64.exe"),
    });

    if (rubymine.empty())
        rubymine = findExecutable(under(local, "JetBrains/Toolbox/apps"), "rubymine64.exe");

    add(ExternalApplicationId::rubymine, "RubyMine", std::move(rubymine));

    std::filesystem::path goland = firstExisting({
        under(program_files, "JetBrains/GoLand 2026.1/bin/goland64.exe"),
        under(program_files, "JetBrains/GoLand 2025.3/bin/goland64.exe"),
        under(program_files, "JetBrains/GoLand 2025.2/bin/goland64.exe"),
        under(program_files, "JetBrains/GoLand 2025.1/bin/goland64.exe"),
        under(program_files, "JetBrains/GoLand 2024.3/bin/goland64.exe"),
        under(local, "Programs/GoLand/bin/goland64.exe"),
    });

    if (goland.empty())
        goland = findExecutable(under(local, "JetBrains/Toolbox/apps"), "goland64.exe");

    add(ExternalApplicationId::goland, "GoLand", std::move(goland));

    std::filesystem::path datagrip = firstExisting({
        under(program_files, "JetBrains/DataGrip 2026.1/bin/datagrip64.exe"),
        under(program_files, "JetBrains/DataGrip 2025.3/bin/datagrip64.exe"),
        under(program_files, "JetBrains/DataGrip 2025.2/bin/datagrip64.exe"),
        under(program_files, "JetBrains/DataGrip 2025.1/bin/datagrip64.exe"),
        under(program_files, "JetBrains/DataGrip 2024.3/bin/datagrip64.exe"),
        under(local, "Programs/DataGrip/bin/datagrip64.exe"),
    });

    if (datagrip.empty())
        datagrip = findExecutable(under(local, "JetBrains/Toolbox/apps"), "datagrip64.exe");

    add(ExternalApplicationId::datagrip, "DataGrip", std::move(datagrip));

    add(ExternalApplicationId::jetbrains_fleet, "JetBrains Fleet", firstExisting({
        under(local, "Programs/Fleet/Fleet.exe"),
        under(local, "JetBrains/Fleet/bin/Fleet.exe"),
        under(program_files, "JetBrains/Fleet/Fleet.exe"),
    }));

    // -------------------------------------------------------------------------
    // Lightweight / general editors
    // -------------------------------------------------------------------------

    add(ExternalApplicationId::sublime_text, "Sublime Text", firstExisting({
        under(program_files, "Sublime Text/sublime_text.exe"),
        under(program_files, "Sublime Text 3/sublime_text.exe"),
        under(program_files_x86, "Sublime Text/sublime_text.exe"),
        under(local, "Programs/Sublime Text/sublime_text.exe"),
        under(user_profile, "scoop/apps/sublime-text/current/sublime_text.exe"),
    }));

    add(ExternalApplicationId::notepad_plus_plus, "Notepad++", firstExisting({
        under(program_files, "Notepad++/notepad++.exe"),
        under(program_files_x86, "Notepad++/notepad++.exe"),
        under(user_profile, "scoop/apps/notepadplusplus/current/notepad++.exe"),
        under(chocolatey, "lib/notepadplusplus/tools/notepad++.exe"),
    }));

    add(ExternalApplicationId::atom, "Atom", firstExisting({
        under(local, "atom/atom.exe"),
        under(local, "Programs/Atom/atom.exe"),
        under(program_files, "Atom/atom.exe"),
    }));

    add(ExternalApplicationId::pulsar, "Pulsar", firstExisting({
        under(local, "Programs/Pulsar/Pulsar.exe"),
        under(program_files, "Pulsar/Pulsar.exe"),
    }));

    add(ExternalApplicationId::brackets, "Brackets", firstExisting({
        under(program_files, "Brackets/Brackets.exe"),
        under(program_files_x86, "Brackets/Brackets.exe"),
    }));

    add(ExternalApplicationId::lapce, "Lapce", firstExisting({
        under(local, "Programs/Lapce/Lapce.exe"),
        under(program_files, "Lapce/Lapce.exe"),
        under(user_profile, "scoop/apps/lapce/current/lapce.exe"),
    }));

    add(ExternalApplicationId::lite_xl, "Lite XL", firstExisting({
        under(program_files, "Lite XL/lite-xl.exe"),
        under(program_files_x86, "Lite XL/lite-xl.exe"),
        under(user_profile, "scoop/apps/lite-xl/current/lite-xl.exe"),
    }));

    add(ExternalApplicationId::geany, "Geany", firstExisting({
        under(program_files, "Geany/bin/geany.exe"),
        under(program_files_x86, "Geany/bin/geany.exe"),
        under(user_profile, "scoop/apps/geany/current/bin/geany.exe"),
    }));

    add(ExternalApplicationId::kate, "Kate", firstExisting({
        under(program_files, "Kate/bin/kate.exe"),
        under(program_files_x86, "Kate/bin/kate.exe"),
        under(user_profile, "scoop/apps/kate/current/bin/kate.exe"),
    }));

    // -------------------------------------------------------------------------
    // C/C++ / Java / general IDEs
    // -------------------------------------------------------------------------

    add(ExternalApplicationId::qt_creator, "Qt Creator", firstExisting({
        under(program_files, "Qt/Tools/QtCreator/bin/qtcreator.exe"),
        under(program_files_x86, "Qt/Tools/QtCreator/bin/qtcreator.exe"),
        under(user_profile, "scoop/apps/qt-creator/current/bin/qtcreator.exe"),
    }));

    add(ExternalApplicationId::codeblocks, "Code::Blocks", firstExisting({
        under(program_files, "CodeBlocks/codeblocks.exe"),
        under(program_files_x86, "CodeBlocks/codeblocks.exe"),
    }));

    add(ExternalApplicationId::dev_cpp, "Dev-C++", firstExisting({
        under(program_files, "Embarcadero/Dev-Cpp/devcpp.exe"),
        under(program_files_x86, "Embarcadero/Dev-Cpp/devcpp.exe"),
        under(program_files, "Dev-Cpp/devcpp.exe"),
        under(program_files_x86, "Dev-Cpp/devcpp.exe"),
    }));

    add(ExternalApplicationId::eclipse, "Eclipse", firstExisting({
        under(user_profile, "eclipse/java-latest-released/eclipse/eclipse.exe"),
        under(program_files, "Eclipse Foundation/eclipse/eclipse.exe"),
        under(program_files, "Eclipse/eclipse.exe"),
        under(local, "Programs/Eclipse/eclipse.exe"),
    }));

    add(ExternalApplicationId::netbeans, "NetBeans", firstExisting({
        under(program_files, "NetBeans-25/netbeans/bin/netbeans64.exe"),
        under(program_files, "NetBeans-24/netbeans/bin/netbeans64.exe"),
        under(program_files, "NetBeans-23/netbeans/bin/netbeans64.exe"),
        under(program_files, "NetBeans/netbeans/bin/netbeans64.exe"),
        under(program_files_x86, "NetBeans/netbeans/bin/netbeans.exe"),
    }));

    // -------------------------------------------------------------------------
    // Terminal editors
    // -------------------------------------------------------------------------

    add(ExternalApplicationId::vim, "Vim", firstExisting({
        under(program_files, "Vim/vim91/gvim.exe"),
        under(program_files, "Vim/vim90/gvim.exe"),
        under(program_files_x86, "Vim/vim91/gvim.exe"),
        under(program_files_x86, "Vim/vim90/gvim.exe"),
        under(user_profile, "scoop/apps/vim/current/gvim.exe"),
    }));

    add(ExternalApplicationId::neovim, "Neovim", firstExisting({
        under(program_files, "Neovim/bin/nvim-qt.exe"),
        under(program_files, "Neovim/bin/nvim.exe"),
        under(local, "Programs/Neovim/bin/nvim-qt.exe"),
        under(local, "Programs/Neovim/bin/nvim.exe"),
        under(user_profile, "scoop/apps/neovim/current/bin/nvim.exe"),
    }));

    add(ExternalApplicationId::emacs, "Emacs", firstExisting({
        under(program_files, "Emacs/x86_64/bin/runemacs.exe"),
        under(program_files, "Emacs/bin/runemacs.exe"),
        under(program_files_x86, "Emacs/bin/runemacs.exe"),
        under(user_profile, "scoop/apps/emacs/current/bin/runemacs.exe"),
    }));

    add(ExternalApplicationId::helix, "Helix", firstExisting({
        under(program_files, "helix/hx.exe"),
        under(program_files_x86, "helix/hx.exe"),
        under(user_profile, "scoop/apps/helix/current/hx.exe"),
    }));
}

const ExternalApplication *ApplicationManager::application(ExternalApplicationId id) const
{
    const auto found = std::find_if(applications_.begin(), applications_.end(),
                                    [id](const ExternalApplication &application)
                                    {
                                        return application.id == id;
                                    });

    return found == applications_.end() ? nullptr : &*found;
}

ExternalApplicationId ApplicationManager::defaultApplication() const
{
    const auto preferred = std::find_if(targets_.begin(), targets_.end(),
                                        [](const LaunchTarget &target)
                                        {
                                            return target.info.available &&
                                                   target.info.id != ExternalApplicationId::file_explorer &&
                                                   target.info.id != ExternalApplicationId::terminal &&
                                                   target.info.id != ExternalApplicationId::git_bash &&
                                                   target.info.id != ExternalApplicationId::wsl;
                                        });

    if (preferred != targets_.end())
        return preferred->info.id;

    const auto available = std::find_if(targets_.begin(), targets_.end(),
                                        [](const LaunchTarget &target)
                                        {
                                            return target.info.available;
                                        });

    return available == targets_.end()
               ? ExternalApplicationId::file_explorer
               : available->info.id;
}

bool ApplicationManager::launch(ExternalApplicationId application,
                                const std::filesystem::path &repository,
                                std::string &error) const
{
    const auto found = std::find_if(targets_.begin(), targets_.end(),
                                    [application](const LaunchTarget &target)
                                    {
                                        return target.info.id == application;
                                    });

    if (found == targets_.end() || !found->info.available)
    {
        error = "The selected application is not installed";
        return false;
    }

    std::vector<std::string> arguments = found->arguments;
    const std::string repository_path = repository.string();

    if (application == ExternalApplicationId::git_bash && !arguments.empty())
    {
        arguments.back() += repository_path;
    }
    else
    {
        arguments.push_back(repository_path);
    }

    const izo::ProcessResult result = izo::LaunchProcess({
        found->info.executable,
        std::move(arguments),
        repository,
        true,
    });

    if (!result)
    {
        error = result.errorMessage.empty() ? "Unable to launch application" : result.errorMessage;
        return false;
    }

    error = "Opened repository in " + found->info.name;
    return true;
}