/******************************************************************************
 * 模块名称：Storage Test Task
 * 模块功能：FTL + FatFS 存储测试，从 defaultTask 迁入，统一由 ch395fTestTask
 *          调度运行。正常产品构建下由 defaultTask 调用，行为与原内联逻辑一致。
 * 作者：王建锋
 * 创建日期：2026-08-26
 ******************************************************************************/

#include <string.h>
#include "main.h"
#include "ff.h"

#define DBG_TAG "[STORAGE-TEST]"
#include "dbg_log.h"
#include "storage_test.h"
#include "nand_ftl.h"
#include "diskio.h"
#include "gd5f2gq5ue.h"

storage_test_stats_t g_storage_stats;

static uint8_t s_perf_buf[4096];

/* TC-STO-01: FTL 掉电恢复（journal 持久化）
 * 写文件并同步落盘，随后模拟掉电（卸载 + 复位 FTL + 重新 resume），再挂载校验内容一致。
 */
static void storage_test_powerloss(void)
{
    static FATFS fs;
    static FIL fil;
    const uint32_t len = 4096;
    uint8_t wbuf[512];
    uint8_t rbuf[512];
    UINT bw, br;
    uint32_t i;
    int ok = 0;

    DBG_INFO("=== Storage Test: TC-STO-01 Power-loss recovery ===");

    if (f_mount(&fs, "", 1) != FR_OK) {
        STORAGE_TEST_CHECK(0, "TC-STO-01 mount");
        return;
    }

    /* Phase 1: 写已知内容文件并同步 */
    for (i = 0; i < sizeof(wbuf); i++) {
        wbuf[i] = (uint8_t)((i * 31 + 7) & 0xFF);
    }
    if (f_open(&fil, "pl.dat", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) {
        for (i = 0; i < len; i += sizeof(wbuf)) {
            f_write(&fil, wbuf, sizeof(wbuf), &bw);
        }
        f_sync(&fil);
        f_close(&fil);
    } else {
        STORAGE_TEST_CHECK(0, "TC-STO-01 f_open(write)");
        return;
    }

    /* 模拟掉电：卸载 + 复位 FTL + 重新 resume */
    f_mount(&fs, "", 0);
    nand_ftl_deinit();
    nand_ftl_init();

    if (f_mount(&fs, "", 1) != FR_OK) {
        STORAGE_TEST_CHECK(0, "TC-STO-01 remount after resume");
        return;
    }

    /* Phase 2: 重新打开校验 */
    if (f_open(&fil, "pl.dat", FA_READ) == FR_OK) {
        ok = 1;
        for (i = 0; i < len; i += sizeof(rbuf)) {
            f_read(&fil, rbuf, sizeof(rbuf), &br);
            for (uint32_t j = 0; j < br; j++) {
                if (rbuf[j] != (uint8_t)(((i + j) * 31 + 7) & 0xFF)) {
                    ok = 0;
                    break;
                }
            }
            if (!ok) break;
        }
        f_close(&fil);
    } else {
        ok = 0;
    }
    STORAGE_TEST_CHECK(ok, "TC-STO-01 content survived power-loss (resume)");
    f_unlink("pl.dat");
}

/* TC-STO-02: 大文件 / 多扇区 / 随机 seek
 * 写 256KB 位置相关模式文件，顺序读回比对 + 随机偏移 seek 读比对。
 */
static void storage_test_largefile(void)
{
    static FATFS fs;
    static FIL fil;
    const uint32_t total = 256 * 1024;
    const uint32_t chunk = 4096;
    uint8_t buf[4096];
    UINT bw, br;
    uint32_t off, i;
    int ok = 1;

    DBG_INFO("=== Storage Test: TC-STO-02 Large file / random seek ===");

    if (f_mount(&fs, "", 1) != FR_OK) {
        STORAGE_TEST_CHECK(0, "TC-STO-02 mount");
        return;
    }

    /* 写：位置相关模式 */
    if (f_open(&fil, "big.dat", FA_CREATE_ALWAYS | FA_WRITE) != FR_OK) {
        STORAGE_TEST_CHECK(0, "TC-STO-02 f_open(write)");
        return;
    }
    for (off = 0; off < total; off += chunk) {
        for (i = 0; i < chunk; i++) {
            buf[i] = (uint8_t)(((off + i) * 31 + 7) & 0xFF);
        }
        f_write(&fil, buf, chunk, &bw);
    }
    f_close(&fil);

    /* 顺序读回 */
    if (f_open(&fil, "big.dat", FA_READ) != FR_OK) {
        STORAGE_TEST_CHECK(0, "TC-STO-02 f_open(read)");
        return;
    }
    for (off = 0; off < total; off += chunk) {
        f_read(&fil, buf, chunk, &br);
        if (br != chunk) { ok = 0; break; }
        for (i = 0; i < chunk; i++) {
            if (buf[i] != (uint8_t)(((off + i) * 31 + 7) & 0xFF)) { ok = 0; break; }
        }
        if (!ok) break;
    }
    STORAGE_TEST_CHECK(ok, "TC-STO-02 sequential 256KB read consistent");

    /* 随机 seek 读 */
    if (ok) {
        uint32_t seek_offs[] = { 0, 33333, 131072, 200000, total - 200 };
        int seek_ok = 1;
        for (int s = 0; s < 5; s++) {
            uint32_t so = seek_offs[s];
            uint32_t sc = 200;
            if (f_lseek(&fil, so) != FR_OK) { seek_ok = 0; break; }
            f_read(&fil, buf, sc, &br);
            for (i = 0; i < sc; i++) {
                if (buf[i] != (uint8_t)(((so + i) * 31 + 7) & 0xFF)) { seek_ok = 0; break; }
            }
            if (!seek_ok) break;
        }
        STORAGE_TEST_CHECK(seek_ok, "TC-STO-02 random seek read consistent");
    }
    f_close(&fil);
    f_unlink("big.dat");
}

/* TC-STO-03: 坏块注入下存储写入
 * 清空旧 map → 注入若干好块为坏（持久化） → 重建文件系统 → 正常写读 → 校验坏块仍被报告。
 */
static void storage_test_badblock(void)
{
    static FATFS fs;
    static FIL fil;
    uint8_t wbuf[512], rbuf[512];
    UINT bw, br;
    uint32_t bad[3];
    int nbad = 0;
    int ok = 1;

    DBG_INFO("=== Storage Test: TC-STO-03 Bad-block injection during storage ===");

    /* 清空旧 dhara map，避免后续注入的坏块被旧映射引用 */
    nand_ftl_format();

    /* 注入：挑出厂好块标记为坏（更新并持久化 BBT） */
    for (uint32_t b = 100; b < gd5f_get_usable_blocks() && nbad < 3; b++) {
        if (gd5f2gq5ue_is_block_bad(b) == 0) {
            gd5f2gq5ue_mark_block_bad(b);
            bad[nbad++] = b;
        }
    }
    if (nbad < 3) {
        STORAGE_TEST_CHECK(0, "TC-STO-03 could not find 3 good blocks (got %d)", nbad);
        return;
    }
    STORAGE_TEST_CHECK(nbad == 3, "TC-STO-03 injected %d bad blocks", nbad);

    /* 复位 FTL 使 dhara 经 is_block_bad 看到新坏块 */
    f_mount(&fs, "", 0);
    nand_ftl_deinit();
    nand_ftl_init();

    /* 重建文件系统（dhara 仍会跳过坏块） */
    {
        MKFS_PARM opts = { FM_FAT32, 0, 0, 0, 0 };
        uint8_t work[FF_MAX_SS];
        if (f_mkfs("", &opts, work, sizeof(work)) != FR_OK) {
            STORAGE_TEST_CHECK(0, "TC-STO-03 f_mkfs");
            return;
        }
    }
    if (f_mount(&fs, "", 1) != FR_OK) {
        STORAGE_TEST_CHECK(0, "TC-STO-03 mount");
        return;
    }

    /* 正常文件写读 */
    for (uint32_t i = 0; i < sizeof(wbuf); i++) {
        wbuf[i] = (uint8_t)((i * 17 + 3) & 0xFF);
    }
    if (f_open(&fil, "bbt.dat", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) {
        f_write(&fil, wbuf, sizeof(wbuf), &bw);
        f_close(&fil);
    } else {
        STORAGE_TEST_CHECK(0, "TC-STO-03 f_open(write)");
        return;
    }

    if (f_open(&fil, "bbt.dat", FA_READ) == FR_OK) {
        f_read(&fil, rbuf, sizeof(rbuf), &br);
        f_close(&fil);
        if (br == sizeof(wbuf)) {
            for (uint32_t i = 0; i < sizeof(rbuf); i++) {
                if (rbuf[i] != wbuf[i]) { ok = 0; break; }
            }
        } else {
            ok = 0;
        }
    } else {
        ok = 0;
    }
    STORAGE_TEST_CHECK(ok, "TC-STO-03 file intact with injected bad blocks");

    /* 校验注入坏块仍被报告为坏（未被 dhara 占用） */
    int badok = 1;
    for (int k = 0; k < nbad; k++) {
        if (gd5f2gq5ue_is_block_bad(bad[k]) != 1) { badok = 0; }
    }
    STORAGE_TEST_CHECK(badok, "TC-STO-03 injected blocks still reported bad");

    f_unlink("bbt.dat");
    /* 注意：注入的坏块已持久化，测试设备专用；如需恢复需重建 BBT / 重新格式化 */
}

#ifdef ENABLE_STORAGE_RECOVERY_TESTS
static void storage_test_recover(void);
#endif

void storage_test_run(void)
{
    static FATFS fs;
    static FIL fil;
    static uint8_t work[FF_MAX_SS];
    char read_buf[64];
    const char *test_str = "Hello FatFS + dhara FTL!";

    memset(&g_storage_stats, 0, sizeof(g_storage_stats));

    /* ==================== FTL + FatFS 文件系统测试 ==================== */

    FRESULT res = f_mount(&fs, "", 1);
    if (res == FR_NO_FILESYSTEM) {
        MKFS_PARM opts = { FM_FAT32, 0, 0, 0, 0 };
        res = f_mkfs("", &opts, work, sizeof(work));
        STORAGE_TEST_CHECK(res == FR_OK, "f_mkfs");
        res = f_mount(&fs, "", 1);
    }
    STORAGE_TEST_CHECK(res == FR_OK, "f_mount");

    if (res == FR_OK) {
        UINT bw, br;

        res = f_open(&fil, "test.txt", FA_CREATE_ALWAYS | FA_WRITE);
        STORAGE_TEST_CHECK(res == FR_OK, "f_open(write)");
        if (res == FR_OK) {
            f_write(&fil, test_str, (UINT)strlen(test_str), &bw);
            STORAGE_TEST_CHECK(bw == (UINT)strlen(test_str), "f_write (%u bytes)", bw);
            f_close(&fil);

            res = f_open(&fil, "test.txt", FA_READ);
            STORAGE_TEST_CHECK(res == FR_OK, "f_open(read)");
            if (res == FR_OK) {
                f_read(&fil, read_buf, (UINT)(sizeof(read_buf) - 1), &br);
                f_close(&fil);
                read_buf[br] = '\0';
                STORAGE_TEST_CHECK(br == (UINT)strlen(test_str) &&
                                  memcmp(test_str, read_buf, br) == 0,
                                  "data verify \"%s\"", read_buf);
            }
        }
        f_unlink("test.txt");
    }

    /* ==================== FTL + FatFS 性能测试 ==================== */

    DBG_INFO("=== Storage Test: Performance ===");

    {
        FIL pfile;
        UINT pw, pr;
        uint32_t t0, tw, tr;
        uint32_t i;
        uint32_t total_kb = 128;
        uint32_t chunk = sizeof(s_perf_buf);
        uint32_t count = (total_kb * 1024) / chunk;

        memset(s_perf_buf, 0xA5, sizeof(s_perf_buf));

        /* 写入 */
        t0 = HAL_GetTick();
        if (f_open(&pfile, "perf.dat", FA_CREATE_ALWAYS | FA_WRITE) == FR_OK) {
            for (i = 0; i < count; i++) {
                f_write(&pfile, s_perf_buf, chunk, &pw);
            }
            f_close(&pfile);
        }
        tw = HAL_GetTick() - t0;

        /* 读取 */
        t0 = HAL_GetTick();
        if (f_open(&pfile, "perf.dat", FA_READ) == FR_OK) {
            for (i = 0; i < count; i++) {
                f_read(&pfile, s_perf_buf, chunk, &pr);
            }
            f_close(&pfile);
        }
        tr = HAL_GetTick() - t0;

        f_unlink("perf.dat");

        if (tw > 0) {
            DBG_INFO("  Write: %lu KB in %lu ms = %lu KB/s",
                     total_kb, tw, (total_kb * 1000) / tw);
        }
        if (tr > 0) {
            DBG_INFO("  Read:  %lu KB in %lu ms = %lu KB/s",
                     total_kb, tr, (total_kb * 1000) / tr);
        }
    }

    /* ==================== 存储集成测试（TC-STO-01~03） ==================== */

    storage_test_powerloss();
    storage_test_largefile();
    storage_test_badblock();

#ifdef ENABLE_STORAGE_RECOVERY_TESTS
    storage_test_recover();
#endif

    /* ==================== 汇总报告 ==================== */

    STORAGE_TEST_REPORT("Storage Tests");
}

#ifdef ENABLE_STORAGE_RECOVERY_TESTS
/* TC-STO-04: 重建 BBT + 重新格式化（恢复 TC-STO-03 注入的坏块）
 * 仅在启用 ENABLE_STORAGE_RECOVERY_TESTS 时编译运行；运行一次即可把设备
 * 恢复到干净出厂态（清除运行时注入坏块并清空 dhara map）。
 */
static void storage_test_recover(void)
{
    DBG_INFO("=== Storage Test: TC-STO-04 BBT rebuild + format (recovery) ===");

    int r = gd5f2gq5ue_bbt_rebuild();
    STORAGE_TEST_CHECK(r == GD5F_OK, "TC-STO-04 bbt rebuild");

    nand_ftl_format();
    STORAGE_TEST_CHECK(1, "TC-STO-04 nand_ftl_format done");

    uint8_t fb[GD5F_TOTAL_BLOCKS / 8];
    uint32_t ver = 0;
    if (gd5f_bbt_dump_flash(fb, &ver) == GD5F_OK) {
        STORAGE_TEST_CHECK(ver >= 1, "TC-STO-04 persisted BBT version=%lu", ver);
    } else {
        STORAGE_TEST_CHECK(0, "TC-STO-04 dump_flash failed");
    }

    DBG_INFO("TC-STO-04 recovery complete; recommend power-cycle to re-init cleanly");
}
#endif
