#ifndef MISSION_TMTC_TMSTOREBACKEND_H_
#define MISSION_TMTC_TMSTOREBACKEND_H_

#include <fsfw/ipc/CommandMessageIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/storagemanager/StorageManagerIF.h>
#include <fsfw/tmstorage/TmStoreFrontendSimpleIF.h>
#include <fsfw/tmtcpacket/pus/tm/PusTmReader.h>
#include <fsfw/tmtcservices/AcceptsTelemetryIF.h>
#include <mission/memory/SdCardMountedIF.h>

#include <filesystem>

#include "eive/eventSubsystemIds.h"
#include "eive/resultClassIds.h"

enum class RolloverInterval { MINUTELY, HOURLY, DAILY };

struct PersistentTmStoreArgs {
  PersistentTmStoreArgs(object_id_t objectId, const char* baseDir, std::string baseName,
                        RolloverInterval intervalUnit, uint32_t intervalCount,
                        StorageManagerIF& tmStore, SdCardMountedIF& sdcMan)
      : objectId(objectId),
        baseDir(baseDir),
        baseName(baseName),
        intervalUnit(intervalUnit),
        intervalCount(intervalCount),
        tmStore(tmStore),
        sdcMan(sdcMan) {}

  object_id_t objectId;
  const char* baseDir;
  std::string baseName;
  RolloverInterval intervalUnit;
  uint32_t intervalCount;
  StorageManagerIF& tmStore;
  SdCardMountedIF& sdcMan;
};

class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
 public:
  enum class State { IDLE, DUMPING };
  static constexpr uint8_t INTERFACE_ID = CLASS_ID::PERSISTENT_TM_STORE;
  static constexpr ReturnValue_t DUMP_DONE = returnvalue::makeCode(INTERFACE_ID, 0);
  static constexpr ReturnValue_t BUSY_DUMPING = returnvalue::makeCode(INTERFACE_ID, 1);

  PersistentTmStore(PersistentTmStoreArgs args);

  ReturnValue_t initializeTmStore();
  State getState() const;
  ReturnValue_t handleCommandQueue(StorageManagerIF& ipcStore, Command_t& execCmd);

  void deleteUpTo(uint32_t unixSeconds);
  ReturnValue_t startDumpFrom(uint32_t fromUnixSeconds);
  ReturnValue_t startDumpFromUpTo(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds);
  /**
   *
   * @param tmReader: Next packet will be loaded into the PUS TM reader. A CRC check will be
   *    performed on the packet. If that check fails, the file is considered corrupted and will
   *    be deleted for now.
   * @param fileHasSwapped: If the CRC check fails, the file will be deleted and a new one has to
   *    be loaded. The dump can reach completion during that process. If a file is swapped, this
   *    boolean is set to true
   * @return DUMP_DONE if dump is finished, returnvalue::OK if the next packet was loaded into the
   *    TM reader, and the returnvalue of the file swap operation if the CRC check failed and
   *    a new file was loaded.
   */
  ReturnValue_t getNextDumpPacket(PusTmReader& tmReader, bool& fileHasSwapped);
  /**
   * Confirm the dump to advance the dump state machine.
   * @param tmReader
   * @param fileHasSwapped: If the confirmed dumps completes the current file, a new file will
   *    be loaded and this parameter will be set to true.
   * @return If a file is swapped, the retrunvalue of the file swap operation.
   */
  ReturnValue_t confirmDump(const PusTmReader& tmReader, bool& fileHasSwapped);

  void getStartAndEndTimeCurrentOrLastDump(uint32_t& startTime, uint32_t& endTime) const;
  ReturnValue_t storePacket(PusTmReader& reader);
  ReturnValue_t cancelDump();

 protected:
  StorageManagerIF& tmStore;

 private:
  static constexpr uint8_t MAX_FILES_IN_ONE_SECOND = 10;
  static constexpr size_t MAX_FILESIZE = 8192;

  //! [EXPORT] : [SKIP]
  static constexpr ReturnValue_t INVALID_FILE_DETECTED_AND_DELETED = returnvalue::makeCode(2, 1);

  MessageQueueIF* tcQueue;
  State state = State::IDLE;
  bool baseDirUninitialized = true;
  const char* baseDir;
  std::string baseName;
  uint8_t currentSameSecNumber = 0;
  std::filesystem::path basePath;
  uint32_t rolloverDiffSeconds = 0;
  std::array<uint8_t, MAX_FILESIZE> fileBuf{};
  timeval currentTv;
  timeval activeFileTv{};

  struct ActiveDumpParams {
    bool pendingPacketDump = false;
    uint32_t fromUnixTime = 0;
    uint32_t untilUnixTime = 0;
    uint32_t currentFileUnixStamp = 0;
    std::filesystem::directory_iterator dirIter;
    std::filesystem::directory_entry dirEntry;
    size_t fileSize = 0;
    size_t currentSize = 0;
  };
  ActiveDumpParams dumpParams;
  std::optional<std::filesystem::path> activeFile;
  SdCardMountedIF& sdcMan;

  /**
   * To get the queue where commands shall be sent.
   * @return  Id of command queue.
   */
  [[nodiscard]] MessageQueueId_t getCommandQueue() const override;

  void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount);
  ReturnValue_t createMostRecentFile(std::optional<uint8_t> suffix);
  static ReturnValue_t pathToTime(const std::filesystem::path& path, struct tm& time);
  void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp);
  ReturnValue_t loadNextDumpFile();
  bool updateBaseDir();
  ReturnValue_t assignAndOrCreateMostRecentFile();
};

#endif /* MISSION_TMTC_TMSTOREBACKEND_H_ */