// Copyright Contributors to the DNF5 project.
// Copyright Contributors to the libdnf project.
// SPDX-License-Identifier: LGPL-2.1-or-later
//
// This file is part of libdnf: https://github.com/rpm-software-management/libdnf/
//
// Libdnf is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or
// (at your option) any later version.
//
// Libdnf is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with libdnf.  If not, see <https://www.gnu.org/licenses/>.

#ifndef LIBDNF5_BASE_BASE_IMPL_HPP
#define LIBDNF5_BASE_BASE_IMPL_HPP

#include "../advisory/advisory_sack.hpp"
#include "plugin/plugins.hpp"
#include "system/state.hpp"

#include "libdnf5/base/base.hpp"
#include "libdnf5/utils/locker.hpp"
#ifdef WITH_MODULEMD
#include "libdnf5/module/module_sack.hpp"
#endif


namespace libdnf5 {

namespace solv {

class CompsPool;
class RpmPool;

}  // namespace solv

class Base::Impl {
public:
    /// @return The system state object.
    /// @since 5.0
    libdnf5::system::State & get_system_state();
    libdnf5::advisory::AdvisorySackWeakPtr get_rpm_advisory_sack() { return rpm_advisory_sack.get_weak_ptr(); }

    solv::RpmPool & get_rpm_pool() {
        libdnf_user_assert(pool, "Base instance was not fully initialized by Base::setup()");
        return *pool;
    }

    solv::CompsPool & get_comps_pool() {
        libdnf_user_assert(comps_pool, "Base instance was not fully initialized by Base::setup()");
        return *comps_pool;
    }

    plugin::Plugins & get_plugins() { return plugins; }

    std::vector<plugin::PluginInfo> & get_plugins_info() { return plugins_info; }

    const std::vector<plugin::PluginInfo> & get_plugins_info() const { return plugins_info; }

    /// Call a function that loads the config file, catching errors appropriately
    void with_config_file_path(std::function<void(const std::string &)> func);

    bool lock_system_repo(libdnf5::utils::LockAccess type, libdnf5::utils::LockBlocking blocking);
    void unlock_system_repo();
    const libdnf5::utils::Locker * get_system_repo_lock() const noexcept;

private:
    friend class Base;
    Impl(const libdnf5::BaseWeakPtr & base, std::vector<std::unique_ptr<Logger>> && loggers);

    bool repos_configured{false};

    // RpmPool as the owner of underlying libsolv data, has to be the first member so that it is destroyed last.
    std::unique_ptr<solv::RpmPool> pool;

    // In libsolv the groups and environmental groups are stored as regular
    // solvables (just with "group:" / "environment:" prefix in their name).
    // These group solvables then contain hard requirements for included
    // mandatory packages. But dnf5's groups behavior is less restrictive - we
    // allow to install group without having any of it's packages installed.
    // When groups (especially the installed ones in @System repo) are in main
    // pool, they can block removals of mandatory group packages.
    // Thus we need to keep group solvables in a separate pool.
    std::unique_ptr<solv::CompsPool> comps_pool;

    std::optional<libdnf5::system::State> system_state;
    libdnf5::advisory::AdvisorySack rpm_advisory_sack;

    plugin::Plugins plugins;
    LogRouter log_router;
    ConfigMain config;
    comps::CompsSack comps_sack;
    repo::RepoSack repo_sack;
    rpm::PackageSack rpm_package_sack;
#ifdef WITH_MODULEMD
    module::ModuleSack module_sack;
#endif
    std::map<std::string, std::string> variables;
    transaction::TransactionHistory transaction_history;
    Vars vars;
    std::unique_ptr<repo::DownloadCallbacks> download_callbacks;
    std::optional<libdnf5::utils::Locker> system_repo_lock;

    /// map of plugin names (global patterns) that we want to enable (true) or disable (false)
    PreserveOrderMap<std::string, bool> plugins_enablement;
    std::vector<plugin::PluginInfo> plugins_info;

    WeakPtrGuard<LogRouter, false> log_router_guard;
    WeakPtrGuard<Vars, false> vars_guard;
};


class InternalBaseUser {
public:
    static solv::CompsPool & get_comps_pool(const libdnf5::BaseWeakPtr & base) {
        return base->p_impl->get_comps_pool();
    }

    static std::vector<plugin::PluginInfo> & get_plugins_info(Base * base) { return base->p_impl->get_plugins_info(); }

    static solv::RpmPool & get_rpm_pool(const libdnf5::BaseWeakPtr & base) { return base->p_impl->get_rpm_pool(); }
};

}  // namespace libdnf5

#endif  // LIBDNF5_BASE_BASE_IMPL_HPP
