// ============================================================================
// test/test_batch_e/test_batch_e.cpp
//
// Unity tests for Batch-E firmware modules:
//   Module 1: src/handset_fingerprint.{h,cpp} — known-camera-vendor detection
//             (the whitehat pivot that replaced the disabled hidden_tx_scanner
//             continuous-carrier concept -- see handset_fingerprint.h for the
//             design notes and hidden_tx_scanner.h for why that approach was
//             abandoned).
//
// Camera-vendor OUIs used below are REAL entries pulled directly from
// oui_table.h (the actual embedded IEEE MA-L registry), not invented
// prefixes -- so a false pass here (test data that happens not to exercise
// the real lookup table) is not possible:
//   0x2CAA8E -> "Wyze Labs Inc"      (camera vendor, kCameraVendorSubstrings)
//   0x0002D1 -> "Vivotek, Inc."      (camera vendor)
//   0x000393 -> "Apple, Inc."        (control: real vendor, NOT a camera vendor)
//
// Run with:
//   pio test -e native -f test_batch_e
// ============================================================================

#include <unity.h>
#include <cstdint>
#include <cstring>

// ---------------------------------------------------------------------------
// Include implementation under test (single translation unit, same pattern
// as test_batch_d). ARDUINO is not defined in the native env, so
// handset_fingerprint.cpp uses its std::chrono-based hfp_now_ms() path --
// no millis() stub needed.
// ---------------------------------------------------------------------------
#include "../../src/handset_fingerprint.cpp"

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

// Builds a minimal 26-byte 802.11 probe-request (24-byte mgmt header +
// one zero-length SSID IE) -- handset_fingerprint_process_wifi_probe()
// requires at least MGMT_HDR_LEN+2 bytes to have room for a single IE
// tag/len pair, even an empty one. Source MAC (Address 2) is at bytes
// 10..15; with no vendor-IE present, vendor resolution falls back to the
// source MAC's own OUI.
static void build_min_probe(uint8_t frame[26], const uint8_t mac[6]) {
    memset(frame, 0, 26);
    memcpy(frame + 10, mac, 6);
    frame[24] = 0; // tag 0 = SSID
    frame[25] = 0; // length 0
}

void setUp(void) { handset_fingerprint_init(); }
void tearDown(void) {}

// ---------------------------------------------------------------------------
// is_camera_vendor() unit-level checks (direct, white-box -- same
// translation unit)
// ---------------------------------------------------------------------------
void test_camera_vendor_substring_matches(void) {
    TEST_ASSERT_TRUE(is_camera_vendor("Wyze Labs Inc"));
    TEST_ASSERT_TRUE(is_camera_vendor("Zhejiang Dahua "));
    TEST_ASSERT_TRUE(is_camera_vendor("Prama Hikvision"));
    TEST_ASSERT_TRUE(is_camera_vendor("Vivotek, Inc."));
    TEST_ASSERT_TRUE(is_camera_vendor("Amcrest Technol"));
}

void test_camera_vendor_non_camera_vendor_not_flagged(void) {
    TEST_ASSERT_FALSE(is_camera_vendor("Apple, Inc."));
    TEST_ASSERT_FALSE(is_camera_vendor("Samsung Electro"));
    TEST_ASSERT_FALSE(is_camera_vendor(""));
    TEST_ASSERT_FALSE(is_camera_vendor(nullptr));
}

void test_camera_vendor_case_insensitive(void) {
    TEST_ASSERT_TRUE(is_camera_vendor("HIKVISION CORP"));
    TEST_ASSERT_TRUE(is_camera_vendor("dahua technology"));
}

// ---------------------------------------------------------------------------
// End-to-end: real WiFi probe frame from a real camera-vendor OUI must come
// out of handset_fingerprint_list_devices() flagged is_known_camera_vendor.
// ---------------------------------------------------------------------------
void test_wifi_probe_from_camera_oui_is_flagged(void) {
    uint8_t mac[6] = {0x2C, 0xAA, 0x8E, 0x01, 0x02, 0x03}; // Wyze Labs Inc
    uint8_t frame[26];
    build_min_probe(frame, mac);

    handset_fingerprint_process_wifi_probe(frame, sizeof(frame), -55);

    HandsetDeviceInfo devs[8];
    size_t n = handset_fingerprint_list_devices(devs, 8);
    TEST_ASSERT_EQUAL_UINT32(1, (uint32_t)n);
    TEST_ASSERT_EQUAL_STRING("Wyze Labs Inc", devs[0].vendor);
    TEST_ASSERT_TRUE_MESSAGE(devs[0].is_known_camera_vendor,
                              "Wyze Labs Inc OUI should be flagged as a known camera vendor");

    HandsetStats stats{};
    handset_fingerprint_get_stats(&stats);
    TEST_ASSERT_EQUAL_UINT16(1, stats.camera_vendor_matches_total);
}

// A non-camera real vendor (Apple) must NOT be flagged, and must not
// increment the camera-vendor counter -- proves the check is discriminating,
// not a stub that always returns true.
void test_wifi_probe_from_noncamera_oui_not_flagged(void) {
    uint8_t mac[6] = {0x00, 0x03, 0x93, 0x11, 0x22, 0x33}; // Apple, Inc.
    uint8_t frame[26];
    build_min_probe(frame, mac);

    handset_fingerprint_process_wifi_probe(frame, sizeof(frame), -60);

    HandsetDeviceInfo devs[8];
    size_t n = handset_fingerprint_list_devices(devs, 8);
    TEST_ASSERT_EQUAL_UINT32(1, (uint32_t)n);
    TEST_ASSERT_EQUAL_STRING("Apple, Inc.", devs[0].vendor);
    TEST_ASSERT_FALSE_MESSAGE(devs[0].is_known_camera_vendor,
                               "Apple, Inc. OUI must not be flagged as a camera vendor");

    HandsetStats stats{};
    handset_fingerprint_get_stats(&stats);
    TEST_ASSERT_EQUAL_UINT16(0, stats.camera_vendor_matches_total);
}

// BLE path (public address, resolved via source-MAC OUI fallback) must be
// flagged the same way as the WiFi path -- both call sites share note_device().
void test_ble_public_addr_from_camera_oui_is_flagged(void) {
    uint8_t mac[6] = {0x00, 0x02, 0xD1, 0x44, 0x55, 0x66}; // Vivotek, Inc.
    uint8_t adv[1] = {0x00}; // empty AD structure, forces OUI fallback path

    handset_fingerprint_process_ble_adv(mac, adv, 0, -50, /*is_public_addr=*/true);

    HandsetDeviceInfo devs[8];
    size_t n = handset_fingerprint_list_devices(devs, 8);
    TEST_ASSERT_EQUAL_UINT32(1, (uint32_t)n);
    TEST_ASSERT_EQUAL_STRING("Vivotek, Inc.", devs[0].vendor);
    TEST_ASSERT_TRUE(devs[0].is_known_camera_vendor);
}

// A BLE random address must never resolve a vendor at all (note_device()'s
// documented is_public_addr gate) -- so it must also never be flagged as a
// camera vendor, even if the raw bytes happen to collide with a real OUI.
void test_ble_random_addr_from_camera_oui_bytes_not_flagged(void) {
    uint8_t mac[6] = {0x2C, 0xAA, 0x8E, 0x77, 0x88, 0x99}; // same Wyze OUI bytes
    uint8_t adv[1] = {0x00};

    handset_fingerprint_process_ble_adv(mac, adv, 0, -50, /*is_public_addr=*/false);

    HandsetDeviceInfo devs[8];
    size_t n = handset_fingerprint_list_devices(devs, 8);
    TEST_ASSERT_EQUAL_UINT32(1, (uint32_t)n);
    TEST_ASSERT_EQUAL_STRING("", devs[0].vendor);
    TEST_ASSERT_FALSE(devs[0].is_known_camera_vendor);
}

// Repeated sightings of the SAME device must not double-count the camera
// stat (mirrors the existing vendor_matches_total / controller_app_matches_total
// once-per-device counting behavior in note_device()).
void test_camera_stat_counts_distinct_devices_not_sightings(void) {
    uint8_t mac[6] = {0x2C, 0xAA, 0x8E, 0xAA, 0xBB, 0xCC}; // Wyze Labs Inc
    uint8_t frame[26];
    build_min_probe(frame, mac);

    handset_fingerprint_process_wifi_probe(frame, sizeof(frame), -55);
    handset_fingerprint_process_wifi_probe(frame, sizeof(frame), -54);
    handset_fingerprint_process_wifi_probe(frame, sizeof(frame), -56);

    HandsetStats stats{};
    handset_fingerprint_get_stats(&stats);
    TEST_ASSERT_EQUAL_UINT16(1, stats.camera_vendor_matches_total);
    TEST_ASSERT_EQUAL_UINT32(3, stats.wifi_probes_seen);
}

// ===========================================================================
// Unity main
// ===========================================================================
int main(int, char**) {
    UNITY_BEGIN();

    RUN_TEST(test_camera_vendor_substring_matches);
    RUN_TEST(test_camera_vendor_non_camera_vendor_not_flagged);
    RUN_TEST(test_camera_vendor_case_insensitive);
    RUN_TEST(test_wifi_probe_from_camera_oui_is_flagged);
    RUN_TEST(test_wifi_probe_from_noncamera_oui_not_flagged);
    RUN_TEST(test_ble_public_addr_from_camera_oui_is_flagged);
    RUN_TEST(test_ble_random_addr_from_camera_oui_bytes_not_flagged);
    RUN_TEST(test_camera_stat_counts_distinct_devices_not_sightings);

    return UNITY_END();
}
