--!A cross-platform build utility based on Lua
--
-- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License.
-- You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
-- Copyright (C) 2015-present, Xmake Open Source Community.
--
-- @author      ruki
-- @file        install_package.lua
--

-- imports
import("core.base.option")
import("core.base.json")
import("core.base.semver")
import("lib.detect.find_tool")
import("package.manager.vcpkg.configurations")
import("package.manager.vcpkg.utils", {alias = "vcpkg_utils"})

-- install for classic mode
function _install_for_classic(vcpkg, name, opt)

    -- get configs
    local configs = opt.configs or {}

    -- init triplet
    local arch = opt.arch
    local plat = opt.plat
    plat = configurations.plat(plat)
    arch = configurations.arch(arch)
    local triplet = configurations.triplet(configs, plat, arch)

    -- init argv
    local argv = {"install", name .. ":" .. triplet}
    if option.get("diagnosis") then
        table.insert(argv, "--debug")
    end

    -- check if the base package is already installed with different features,
    -- if so, prompt user before rebuilding with --recurse
    -- @see https://github.com/xmake-io/xmake/issues/7388
    local basename = name:gsub("%[.-%]", "")
    if basename ~= name then
        if not vcpkg_utils.is_installed(vcpkg, name, triplet, opt) then
            if vcpkg_utils.is_installed(vcpkg, basename, triplet, opt) then
                local confirm = utils.confirm({default = true,
                    description = format("%s:%s is already installed (possibly with different features). Installing %s will require a rebuild of it and its dependencies. Continue?", basename, triplet, name)})
                if confirm then
                    table.insert(argv, "--recurse")
                else
                    raise("install %s:%s cancelled!", name, triplet)
                end
            end
        end
    end

    -- install package
    -- run in a neutral directory so that a project-owned vcpkg.json in the current working
    -- directory does not switch vcpkg into manifest mode (which rejects package arguments).
    -- @see https://github.com/xmake-io/xmake/issues/7660
    os.vrunv(vcpkg, argv, {curdir = vcpkg_utils.classic_curdir()})
end

-- install for manifest mode
function _install_for_manifest(vcpkg, name, opt)

    -- get configs
    local configs = opt.configs or {}

    -- init triplet
    local arch = opt.arch
    local plat = opt.plat
    plat = configurations.plat(plat)
    arch = configurations.arch(arch)
    local triplet = configurations.triplet(configs, plat, arch)

    -- init argv
    local argv = {"--feature-flags=\"versions\"", "install", "--x-wait-for-lock", "--triplet", triplet}
    if option.get("diagnosis") then
        table.insert(argv, "--debug")
    end

    -- generate platform
    local platform = plat .. " & " .. arch

    -- generate dependencies
    local require_version = opt.require_version
    if require_version == "latest" then
        require_version = nil
    end
    local minversion = require_version
    if minversion and minversion:startswith(">=") then
        minversion = minversion:sub(3)
    end
    local dependencies = {}
    table.insert(dependencies, {
        name = name,
        ["version>="] = minversion,
        platform = platform,
        features = configs.features,
        ["default-features"] = configs.default_features})

    -- generate overrides to use fixed version
    local overrides
    if require_version and semver.is_valid(require_version) then
        overrides = {{name = name, version = require_version}}
    end

    -- generate manifest, vcpkg.json
    local baseline = configs.baseline or "2e6fcc44573d091af0321f99c89b212997a76f1f" -- 2025.09.27
    local manifest = {
        name = "stub",
        version = "1.0",
        dependencies = dependencies,
        ["builtin-baseline"] = baseline,
        overrides = overrides}
    local installdir = assert(opt.installdir, "installdir not found!")
    json.savefile(path.join(installdir, "vcpkg.json"), manifest)
    if not os.isdir(installdir) then
        os.mkdir(installdir)
    end
    if option.get("diagnosis") then
        vprint(path.join(installdir, "vcpkg.json"))
        vprint(manifest)
    end

    -- generate vcpkg-configuration.json
    -- @see https://github.com/xmake-io/xmake/issues/2469
    if configs.registries or configs.default_registries then
        local configuration = {registries = configs.registries, ["default-registries"] = configs.default_registries}
        json.savefile(path.join(installdir, "vcpkg-configuration.json"), configuration)
    end

    -- install package
    os.vrunv(vcpkg, argv, {curdir = installdir})
end

-- install package
--
-- @param name  the package name, e.g. pcre2, pcre2/libpcre2-8
-- @param opt   the options, e.g. {verbose = true}
--
function main(name, opt)

    -- attempt to find vcpkg
    local vcpkg = find_tool("vcpkg")
    if not vcpkg then
        raise("vcpkg not found!")
    end

    -- do install
    opt = opt or {}
    if vcpkg_utils.need_manifest(opt) then
        _install_for_manifest(vcpkg.program, name, opt)
    else
        _install_for_classic(vcpkg.program, name, opt)
    end
end
