// 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_MODULE_MODULE_ITEM_HPP
#define LIBDNF5_MODULE_MODULE_ITEM_HPP

#include "libdnf5/defs.h"
#include "libdnf5/module/module_dependency.hpp"
#include "libdnf5/module/module_profile.hpp"
#include "libdnf5/module/module_sack_weak.hpp"

#include <string>
#include <vector>

struct _ModulemdModuleStream;

class ModuleTest;


namespace libdnf5::module {


enum class ModuleStatus;


struct ModuleItemId {
public:
    ModuleItemId() = default;
    explicit ModuleItemId(int id) : id(id) {}

    bool operator==(const ModuleItemId & other) const noexcept { return id == other.id; }
    bool operator!=(const ModuleItemId & other) const noexcept { return id != other.id; }

    // Corresponds to solvable id
    int id{0};
};


// Represents one modulemd document (uniquely described by name:stream:version:context:arch, but there can theoretically be more objects with the same NSVCA)
// @replaces libdnf:module/ModuleItem.hpp:class:ModuleItem
class LIBDNF_API ModuleItem {
public:
    bool operator==(const ModuleItem & rhs) const noexcept;
    bool operator!=(const ModuleItem & rhs) const noexcept;
    bool operator<(const ModuleItem & rhs) const noexcept;

    ~ModuleItem();

    /// @return The module name.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getNameCStr()
    std::string get_name() const;

    /// @return The stream name.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getStream()
    std::string get_stream() const;

    /// @return The version.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getVersionNum()
    long long get_version() const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getVersion()
    std::string get_version_str() const;

    /// @return The context.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getContext()
    std::string get_context() const;

    /// @return The arch.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getArch()
    std::string get_arch() const;

    /// @return The "name:stream:version:context:arch" string.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getFullIdentifier()
    std::string get_full_identifier() const;

    /// @return The summary of this ModuleItem.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getSummary()
    std::string get_summary() const;

    /// @return The description of this ModuleItem.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getDescription()
    std::string get_description() const;

    // TODO(pkratoch): Translations for summary and description

    /// @return The list of RPM NEVRAs in this module.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getArtifacts()
    std::vector<std::string> get_artifacts() const;

    /// @return Sorted list of RPM names that are demodularized.
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getDemodularizedRpms()
    std::vector<std::string> get_demodularized_rpms() const;

    /// @return The list of ModuleProfiles matched by name (possibly a globby pattern).
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getProfiles(const std::string &name)
    std::vector<ModuleProfile> get_profiles(const std::string & name) const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getProfiles()
    std::vector<ModuleProfile> get_profiles() const;

    /// @return The default profiles.
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getDefaultProfile()
    std::vector<std::string> get_default_profiles() const;

    /// Get the `static_context` value (needed for proper behaviour of modular solver).
    ///
    /// @return The value of the `static_context` flag in modulemd. If `true`, the context string
    ///         is static and represents a build and runtime configuration for this stream.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getStaticContext()
    bool get_static_context() const;

    /// @return Module dependencies.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getRequires(bool removePlatform=false)
    std::vector<ModuleDependency> get_module_dependencies(bool remove_platform = false) const;

    /// @return The repo id.
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getRepoID()
    const std::string & get_repo_id() const;

    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getId()
    ModuleItemId get_id() const;

    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getYaml()
    std::string get_yaml() const;

    /// Return true if module_item belongs to enabled or default module stream and it is applicable forcurrent system.
    /// The decision is performed by a modular solver.
    // TODO(jmracek) Read a real decision from the modular solver
    bool is_active() const;

    /// @return The status of this ModuleItem.
    /// @since 5.1.5
    ModuleStatus get_status() const;

    /// @return Whether this ModuleItem is the default stream of the module.
    /// @since 5.1.5
    bool is_default() const;

    ModuleItem(const ModuleItem & mpkg);
    ModuleItem & operator=(const ModuleItem & mpkg);
    ModuleItem(ModuleItem && mpkg) noexcept;
    ModuleItem & operator=(ModuleItem && mpkg) noexcept;

private:
    friend class ModuleQuery;
    friend class ModuleSack;
    friend class ModuleMetadata;
    friend ::ModuleTest;

    LIBDNF_LOCAL ModuleItem(
        _ModulemdModuleStream * md_stream, const ModuleSackWeakPtr & module_sack, const std::string & repo_id);

    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getNameCStr()
    LIBDNF_LOCAL const char * get_name_cstr() const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getStreamCStr()
    LIBDNF_LOCAL const char * get_stream_cstr() const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getContextCStr()
    LIBDNF_LOCAL const char * get_context_cstr() const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getArchCStr()
    LIBDNF_LOCAL const char * get_arch_cstr() const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getNameStream()
    LIBDNF_LOCAL std::string get_name_stream() const;
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getNameStreamVersion()
    LIBDNF_LOCAL std::string get_name_stream_version() const;
    /// @return The "name:stream:computed_static_context" string if computed_static_context exists, otherwise,
    ///         the "name:stream:version:context" string.
    LIBDNF_LOCAL std::string get_name_stream_staticcontext() const;
    /// @return The "name:stream:computed_static_context:arch" string if computed_static_context exists, otherwise,
    ///         the "name:stream:version:context:arch" string.
    LIBDNF_LOCAL std::string get_name_stream_staticcontext_arch() const;

    LIBDNF_LOCAL std::vector<ModuleProfile> get_profiles_internal(const char * name) const;

    LIBDNF_LOCAL static std::vector<ModuleDependency> get_module_dependencies(
        _ModulemdModuleStream * md_stream, bool remove_platform);

    // TODO(pkratoch): Make this private once it's not used in tests.
    /// @return The string representing module dependencies (e.g. "ninja;platform:[f29]" or "nodejs:[-11]").
    /// @param md_stream Module stream
    /// @param remove_platform When true, the method will not return dependencies with stream "platform"
    ///                        (default is false).
    /// @since 5.0
    //
    // @replaces libdnf:module/ModuleItem.hpp:method:ModuleItem.getRequires(bool removePlatform=false)
    LIBDNF_LOCAL static std::string get_module_dependencies_string(
        _ModulemdModuleStream * md_stream, bool remove_platform);
    LIBDNF_LOCAL std::string get_module_dependencies_string(bool remove_platform = false) const;

    LIBDNF_LOCAL static std::string get_name_stream(_ModulemdModuleStream * md_stream);

    /// Create solvable with:
    ///     Name: $name:$stream:$context
    ///     Version: $version
    ///     Arch: $arch (if arch is not defined, set "noarch")
    ///     Provides: module($name)
    ///     Provides: module($name:$stream)
    ///     Conflicts: module($name)
    ///     Description: $name:$stream
    LIBDNF_LOCAL void create_solvable();
    LIBDNF_LOCAL void create_dependencies() const;
    LIBDNF_LOCAL void create_solvable_and_dependencies();

    /// @brief Create platform solvable. Intended to be used for autodetecting modular platform ID.
    /// @param module_sack Reference to a modular sack where the target pool with solvables is located.
    /// @param name Platform name.
    /// @param stream Platform stream.
    LIBDNF_LOCAL static void create_platform_solvable(
        const ModuleSackWeakPtr & module_sack, const std::string & name, const std::string & stream);

    LIBDNF_LOCAL libdnf5::module::ModuleSackWeakPtr get_module_sack() const;

    LIBDNF_LOCAL void set_computed_static_context(const std::string & context);

    class LIBDNF_LOCAL Impl;
    std::unique_ptr<Impl> p_impl;
};


}  // namespace libdnf5::module


#endif  // LIBDNF5_MODULE_MODULE_ITEM_HPP
