#pragma once

#include <chrono>
#include <dbus/api/api.h>
#include <dbus/dBus.h>

namespace api::requirement
{
//--------------------------------------------------------------
using TimePoint = std::chrono::system_clock::time_point;
//--------------------------------------------------------------
enum class Status
{
  Draft,        // The requirement is in draft state and has not been finalized
  InReview,     // The requirement is currently being reviewed by stakeholders
  Approved,     // The requirement has been approved and is ready for implementation
  Rejected      // The requirement has been rejected and will not be implemented
};
//--------------------------------------------------------------
struct Metadata
{
  std::string uuid;           // Unique identifier for the requirement, generated automatically
  std::string id;             // Unique identifier for the requirement, provided by the user (e.g., "REQ-001")
  std::string author;         // Name of the person who created the requirement
  TimePoint   created_at;     // Creation timestamp
  TimePoint   updated_at;     // Last update timestamp
};
//--------------------------------------------------------------
struct Details
{
  std::string title;                    // Short name or title of the requirement
  std::string description;             // Detailed description of the requirement
  std::string acceptance_criteria;     // Criteria that must be met for the requirement to be considered complete
  bool        is_smart;                // SMART indicator (Specific, Measurable, Achievable, Relevant, Time-bound)
};
//--------------------------------------------------------------
struct Classification
{
  std::string type;         // Type of the requirement (e.g., "Functional", "Non-Functional", "Performance", etc.)
  std::string category;     // Category of the requirement (e.g., "UI", "Backend", "Security", etc.)
  int         priority;     // Priority level of the requirement (e.g., 1 for highest priority, 5 for lowest)
  Status      status;       // Current status of the requirement (e.g., "Draft", "In Review", "Approved", "Rejected", etc.)
};
//--------------------------------------------------------------
struct Requirement
{
  Metadata       metadata;           // General information about the requirement
  Details        details;            // Detailed information about the requirement
  Classification classification;     // Classification information about the requirement
};
//--------------------------------------------------------------

/* --- */

//--------------------------------------------------------------
struct GetRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.get")>
{
  virtual ~GetRequirementEvent() = default;     // Default destructor

  std::string uuid;     // UUID of the requirement (empty for root-level requirements)

  [[nodiscard]] std::string toString() const override
  {
    return std::format("GetRequirementEvent: uuid={}", uuid);
  }
};
//--------------------------------------------------------------
inline std::expected<Requirement, dBus::ReturnStatus> postGetRequirementRequest(dBus::Bus &bus, const std::string &uuid)
{
  auto eventData = GetRequirementEvent();
  eventData.uuid = std::move(uuid);
  return dBus::api::requestData<decltype(eventData), Requirement>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
using GetRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<GetRequirementEvent, Requirement>>;

/* --- */

//--------------------------------------------------------------
struct GetChildrenRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.getChildren")>
{
  virtual ~GetChildrenRequirementEvent() = default;     // Default destructor

  std::string uuid;     // UUID of the requirement (empty for root-level requirements)

  [[nodiscard]] std::string toString() const override
  {
    return std::format("GetChildrenRequirementEvent: uuid={}", uuid);
  }
};
//--------------------------------------------------------------
inline std::expected<std::vector<Requirement>, dBus::ReturnStatus> postGetChildrenRequirementRequest(dBus::Bus &bus, const std::string &uuid)
{
  auto eventData = GetChildrenRequirementEvent();
  eventData.uuid = std::move(uuid);
  return dBus::api::requestData<decltype(eventData), std::vector<Requirement>>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
using GetChildrenRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<GetChildrenRequirementEvent, std::vector<Requirement>>>;

/* --- */

//--------------------------------------------------------------
struct AddRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.add")>
{
  virtual ~AddRequirementEvent() = default;     // Default destructor

  std::string parentUuid;          // UUID of the parent requirement (empty for root-level requirements)
  Requirement requirementData;     // Data of the requirement to be added

  [[nodiscard]] std::string toString() const override
  {
    return std::format("AddRequirementEvent: parentUuid={}, id={}, title={}",
                       parentUuid,
                       requirementData.metadata.id,
                       requirementData.details.title);
  }
};
//--------------------------------------------------------------
inline dBus::api::PostReturnStatus postAddRequirementRequest(dBus::Bus &bus, const std::string &parentUuid, const Requirement &requirement)
{
  auto eventData            = AddRequirementEvent();
  eventData.parentUuid      = std::move(parentUuid);
  eventData.requirementData = std::move(requirement);
  return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
using AddRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<AddRequirementEvent, void>>;

/* --- */

//--------------------------------------------------------------
struct UpdateRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.update")>
{
  virtual ~UpdateRequirementEvent() = default;     // Default destructor

  Requirement requirementData;     // Data of the requirement to be updated

  [[nodiscard]] std::string toString() const override
  {
    return std::format("UpdateRequirementEvent: id={}, title={}",
                       requirementData.metadata.id,
                       requirementData.details.title);
  }
};
//--------------------------------------------------------------
inline dBus::api::PostReturnStatus postUpdateRequirementRequest(dBus::Bus &bus, const Requirement &requirement)
{
  auto eventData            = UpdateRequirementEvent();
  eventData.requirementData = std::move(requirement);
  return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
using UpdateRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<UpdateRequirementEvent, void>>;

/* --- */

//--------------------------------------------------------------
struct DeleteRequirementEvent : dBus::api::DefaultData<dBus::makeID("requirement.delete")>
{
  virtual ~DeleteRequirementEvent() = default;     // Default destructor

  Requirement requirementData;     // Data of the requirement to be deleted

  [[nodiscard]] std::string toString() const override
  {
    return std::format("DeleteRequirementEvent: id={}, title={}",
                       requirementData.metadata.id,
                       requirementData.details.title);
  }
};
//--------------------------------------------------------------
inline dBus::api::PostReturnStatus postDeleteRequirementRequest(dBus::Bus &bus, const Requirement &requirement)
{
  auto eventData            = DeleteRequirementEvent();
  eventData.requirementData = std::move(requirement);
  return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
}
//--------------------------------------------------------------
using DeleteRequirementMessage_ptr = std::shared_ptr<dBus::RequestMessage<DeleteRequirementEvent, void>>;

/* --- */

/* Post a requirement event to the bus. This function will post a
 * RequirementEvent containing the provided requirement data to the
 * bus, and return a PostReturnStatus indicating whether the post was
 * successful or if there were any issues (e.g., no subscribers,
 * timeout, etc.). */
// inline dBus::api::PostReturnStatus postRequirementRequest(dBus::Bus  &bus,
//                                                           Requirement requirement)
//{
//   auto eventData            = RequirementEvent();
//   eventData.requirementData = std::move(requirement);
//
//   return dBus::api::requestData<decltype(eventData), void>(bus, eventData.getID(), eventData, dBus::MessageCategory::UI);
// }
//--------------------------------------------------------------
}     // namespace api::requirement
