#pragma once

#include <Arduino.h>
#include <SD.h>
#include <SPI.h>
#include <XPowersLib.h>

class SdCardDiag {
 public:
  explicit SdCardDiag(Print& out = Serial);

  bool begin();
  void printReport();

 private:
  struct ProbeBytes {
    uint8_t ffCount = 0;
    uint8_t zeroCount = 0;
    uint8_t otherCount = 0;
    uint8_t bytes[8] = {0};
  };

  struct DirSummary {
    uint32_t fileCount = 0;
    uint32_t dirCount = 0;
    uint64_t fileBytes = 0;
    uint8_t maxDepth = 0;
  };

  struct CardIdentity {
    bool valid = false;
    uint8_t mid = 0;
    char oid[3] = {0};
    char pnm[6] = {0};
    uint8_t prvMajor = 0;
    uint8_t prvMinor = 0;
    uint32_t psn = 0;
    uint16_t mdtYear = 0;
    uint8_t mdtMonth = 0;
    uint8_t raw[16] = {0};
  };

  void logf(const char* fmt, ...);
  void printDivider();
  void forceSpiDeselected();
  bool initPmu();
  void cycleSdRail(uint32_t offMs = 250, uint32_t onSettleMs = 700);
  void printRailStatus();
  void printPinMap();
  void printPinLevels();
  ProbeBytes idleProbe(SPIClass& bus, const char* busName);
  bool tryMount(SPIClass& bus, const char* busName, uint32_t hz);
  bool mountCard();
  void printCardDetails();
  bool readCardIdentity(CardIdentity& identity);
  void printCardIdentity();
  void summarizeDirectory(File dir, DirSummary& summary, uint8_t depth);
  void printSampleEntries(File dir, uint8_t maxEntries);
  uint8_t crc7(const uint8_t* data, size_t len) const;
  bool waitForSpiByte(uint8_t expected, uint32_t timeoutMs);
  const char* manufacturerNameFromMid(uint8_t mid) const;
  const char* cardTypeToString(uint8_t type) const;
  const char* boolToOnOff(bool value) const;

  Print& out_;
  SPIClass hspi_{HSPI};
  SPIClass fspi_{FSPI};
  SPIClass* activeSpi_ = nullptr;
  const char* activeBusName_ = "none";
  uint32_t activeHz_ = 0;
  XPowersLibInterface* pmu_ = nullptr;
  uint32_t logSeq_ = 0;
  bool pmuReady_ = false;
};
