#include "sd_storage.h"

#include "esp_log.h"
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include "driver/sdmmc_host.h"

static const char *TAG = "sd_storage";

// Freenove ESP32-S3 WROOM N16R8 microSD (SDMMC 1-bit). Mirrors
// ui_freenove_allinone/include/ui_freenove_config.h FREENOVE_SDMMC_*.
#define SD_PIN_CMD 38
#define SD_PIN_CLK 39
#define SD_PIN_D0  40

static sdmmc_card_t *s_card = NULL;

esp_err_t sd_storage_mount(void) {
    if (s_card != NULL) {
        return ESP_OK;
    }

    const esp_vfs_fat_sdmmc_mount_config_t mount_cfg = {
        .format_if_mount_failed = false,   // never wipe a user's card
        .max_files = 5,
        .allocation_unit_size = 16 * 1024,
    };

    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    host.flags = SDMMC_HOST_FLAG_1BIT;     // Freenove wires D0 only
    host.max_freq_khz = SDMMC_FREQ_DEFAULT;

    sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
    slot.width = 1;
    slot.clk = SD_PIN_CLK;
    slot.cmd = SD_PIN_CMD;
    slot.d0  = SD_PIN_D0;
    slot.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;

    esp_err_t err = esp_vfs_fat_sdmmc_mount(SD_STORAGE_MOUNT_POINT, &host,
                                            &slot, &mount_cfg, &s_card);
    if (err != ESP_OK) {
        ESP_LOGW(TAG, "mount failed: %s — no card inserted or wiring issue",
                 esp_err_to_name(err));
        s_card = NULL;
        return err;
    }

    ESP_LOGI(TAG, "microSD mounted at %s — %lu MiB (%s)",
             SD_STORAGE_MOUNT_POINT, (unsigned long) sd_storage_capacity_mb(),
             s_card->cid.name);
    return ESP_OK;
}

esp_err_t sd_storage_unmount(void) {
    if (s_card == NULL) {
        return ESP_OK;
    }
    esp_err_t err = esp_vfs_fat_sdcard_unmount(SD_STORAGE_MOUNT_POINT, s_card);
    s_card = NULL;
    return err;
}

bool sd_storage_ready(void) {
    return s_card != NULL;
}

uint32_t sd_storage_capacity_mb(void) {
    if (s_card == NULL) {
        return 0U;
    }
    uint64_t bytes = (uint64_t) s_card->csd.capacity * s_card->csd.sector_size;
    return (uint32_t) (bytes / (1024ULL * 1024ULL));
}
