#pragma once

#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include "tbeam_supreme_adapter.h"

enum class SdWatchState : uint8_t {
  UNKNOWN = 0,
  ABSENT,
  MOUNTED
};

enum class SdEvent : uint8_t {
  NO_CARD,
  CARD_MOUNTED,
  CARD_REMOVED
};

using SdStatusCallback = void (*)(SdEvent event, const char* message);

struct SdWatcherConfig {
  bool enableSdRailCycle = true;
  bool enablePinDumps = true;
  bool recoveryRailCycleOnFullScan = true;
  uint32_t recoveryRailOffMs = 250;
  uint32_t recoveryRailOnSettleMs = 700;
  uint32_t startupWarmupMs = 1500;
  uint32_t pollIntervalAbsentMs = 1000;
  uint32_t pollIntervalMountedMs = 2000;
  uint32_t fullScanIntervalMs = 10000;
  uint8_t votesToPresent = 2;
  uint8_t votesToAbsent = 5;
};

class StartupSdManager {
 public:
  explicit StartupSdManager(Print& serial = Serial);

  bool begin(const SdWatcherConfig& cfg, SdStatusCallback callback = nullptr);
  void update();

  bool isMounted() const { return watchState_ == SdWatchState::MOUNTED; }
  SdWatchState state() const { return watchState_; }

  bool consumeMountedEvent();
  bool consumeRemovedEvent();
  bool forceRemount();

  void printCardInfo();
  bool ensureDirRecursive(const char* path);
  bool rewriteFile(const char* path, const char* payload);
  void permissionsDemo(const char* path);

 private:
  void logf(const char* fmt, ...);
  void notify(SdEvent event, const char* message);
  void forceSpiDeselected();
  void dumpSdPins(const char* tag);
  bool initPmuForSdPower();
  void cycleSdRail(uint32_t offMs = 250, uint32_t onSettleMs = 600);
  bool tryMountWithBus(SPIClass& bus, const char* busName, uint32_t hz, bool verbose);
  bool mountPreferred(bool verbose);
  bool mountCardFullScan();
  bool verifyMountedCard();
  const char* cardTypeToString(uint8_t type);
  void setStateMounted();
  void setStateAbsent();

  Print& serial_;
  SdWatcherConfig cfg_{};
  SdStatusCallback callback_ = nullptr;

  SPIClass sdSpiH_{HSPI};
  SPIClass sdSpiF_{FSPI};
  SPIClass* sdSpi_ = nullptr;
  const char* sdBusName_ = "none";
  uint32_t sdFreq_ = 0;
  XPowersLibInterface* pmu_ = nullptr;

  SdWatchState watchState_ = SdWatchState::UNKNOWN;
  uint8_t presentVotes_ = 0;
  uint8_t absentVotes_ = 0;
  uint32_t lastPollMs_ = 0;
  uint32_t lastFullScanMs_ = 0;
  uint32_t logSeq_ = 0;

  bool mountedEventPending_ = false;
  bool removedEventPending_ = false;
};
