// lua_fuzzer.cpp - Random Lua code generator for security testing
// Milestone 19: Security Testing Suite

#include "lua_fuzzer.h"
#include "lua_sandbox.h"
#include <sstream>
#include <chrono>

namespace mosis {

LuaFuzzer::LuaFuzzer(uint32_t seed) {
    if (seed == 0) {
        seed = static_cast<uint32_t>(
            std::chrono::steady_clock::now().time_since_epoch().count());
    }
    m_rng.seed(seed);
}

LuaFuzzer::~LuaFuzzer() = default;

int LuaFuzzer::RandomInt(int min, int max) {
    std::uniform_int_distribution<int> dist(min, max);
    return dist(m_rng);
}

bool LuaFuzzer::RandomBool() {
    return RandomInt(0, 1) == 1;
}

char LuaFuzzer::RandomChar() {
    static const char chars[] = "abcdefghijklmnopqrstuvwxyz_";
    return chars[RandomInt(0, sizeof(chars) - 2)];
}

std::string LuaFuzzer::GenerateIdentifier() {
    int len = RandomInt(1, 8);
    std::string id;
    id += RandomChar();  // First char must be letter or underscore
    for (int i = 1; i < len; i++) {
        if (RandomBool()) {
            id += RandomChar();
        } else {
            id += static_cast<char>('0' + RandomInt(0, 9));
        }
    }
    return id;
}

std::string LuaFuzzer::GenerateLiteral() {
    int type = RandomInt(0, 4);
    switch (type) {
        case 0:  // nil
            return "nil";
        case 1:  // boolean
            return RandomBool() ? "true" : "false";
        case 2:  // integer
            return std::to_string(RandomInt(-1000, 1000));
        case 3:  // float
            return std::to_string(RandomInt(-100, 100)) + "." +
                   std::to_string(RandomInt(0, 99));
        case 4:  // string
        {
            int len = RandomInt(0, 20);
            std::string s = "\"";
            for (int i = 0; i < len; i++) {
                char c = static_cast<char>(RandomInt(32, 126));
                if (c == '"' || c == '\\') {
                    s += '\\';
                }
                s += c;
            }
            s += "\"";
            return s;
        }
        default:
            return "nil";
    }
}

std::string LuaFuzzer::GenerateOperator() {
    static const char* ops[] = {
        "+", "-", "*", "/", "//", "%", "^",
        "==", "~=", "<", ">", "<=", ">=",
        "and", "or", ".."
    };
    return ops[RandomInt(0, sizeof(ops)/sizeof(ops[0]) - 1)];
}

std::string LuaFuzzer::GenerateTableConstructor(int depth) {
    if (depth > static_cast<int>(m_max_nesting)) {
        return "{}";
    }

    int count = RandomInt(0, 5);
    if (count == 0) {
        return "{}";
    }

    std::ostringstream ss;
    ss << "{";
    for (int i = 0; i < count; i++) {
        if (i > 0) ss << ", ";

        int style = RandomInt(0, 2);
        switch (style) {
            case 0:  // array style
                ss << GenerateExpression(depth + 1);
                break;
            case 1:  // record style
                ss << GenerateIdentifier() << " = " << GenerateExpression(depth + 1);
                break;
            case 2:  // general style
                ss << "[" << GenerateExpression(depth + 1) << "] = "
                   << GenerateExpression(depth + 1);
                break;
        }
    }
    ss << "}";
    return ss.str();
}

std::string LuaFuzzer::GenerateFunctionCall(int depth) {
    // Use only very safe built-in functions
    static const char* funcs[] = {
        "tostring", "tonumber", "type"
    };

    std::ostringstream ss;
    ss << funcs[RandomInt(0, sizeof(funcs)/sizeof(funcs[0]) - 1)];
    ss << "(";
    ss << GenerateLiteral();  // Just use literals, not recursive expressions
    ss << ")";
    return ss.str();
}

std::string LuaFuzzer::GenerateExpression(int depth) {
    if (depth > static_cast<int>(m_max_nesting)) {
        return GenerateLiteral();
    }

    int type = RandomInt(0, 7);
    switch (type) {
        case 0:
        case 1:
            return GenerateLiteral();
        case 2:
            return GenerateIdentifier();
        case 3:  // binary op
            return "(" + GenerateExpression(depth + 1) + " " +
                   GenerateOperator() + " " + GenerateExpression(depth + 1) + ")";
        case 4:  // unary op
        {
            static const char* unary[] = {"-", "not ", "#"};
            return std::string(unary[RandomInt(0, 2)]) +
                   "(" + GenerateExpression(depth + 1) + ")";
        }
        case 5:
            return GenerateTableConstructor(depth + 1);
        case 6:
            return GenerateFunctionCall(depth + 1);
        case 7:  // table access
            return GenerateIdentifier() + "[" + GenerateExpression(depth + 1) + "]";
        default:
            return GenerateLiteral();
    }
}

std::string LuaFuzzer::GenerateStatement(int depth) {
    if (depth > static_cast<int>(m_max_nesting)) {
        return "local _ = nil";
    }

    int type = RandomInt(0, 5);
    std::ostringstream ss;

    switch (type) {
        case 0:  // local assignment
            ss << "local " << GenerateIdentifier() << " = " << GenerateLiteral();
            break;
        case 1:  // assignment (to local)
            ss << "local " << GenerateIdentifier() << " = " << GenerateExpression(depth + 1);
            break;
        case 2:  // function call statement
            ss << GenerateFunctionCall(depth + 1);
            break;
        case 3:  // if statement (simple)
            ss << "if " << GenerateLiteral() << " then\n"
               << "  local _ = " << GenerateLiteral() << "\n"
               << "end";
            break;
        case 4:  // for numeric (small)
            ss << "for " << GenerateIdentifier() << " = 1, " << RandomInt(1, 5) << " do\n"
               << "  local _ = " << GenerateLiteral() << "\n"
               << "end";
            break;
        case 5:  // do block
            ss << "do\n"
               << "  local " << GenerateIdentifier() << " = " << GenerateLiteral() << "\n"
               << "end";
            break;
        default:
            ss << "local _ = nil";
    }

    return ss.str();
}

std::string LuaFuzzer::GenerateRandomCode() {
    std::ostringstream ss;

    // Generate 1-5 statements
    int count = RandomInt(1, 5);
    for (int i = 0; i < count; i++) {
        ss << GenerateStatement(0) << "\n";
    }

    // Optionally add a return
    if (RandomBool()) {
        ss << "return " << GenerateExpression(0) << "\n";
    }

    return ss.str();
}

bool LuaFuzzer::VerifySandboxIntegrity() {
    // Create a fresh sandbox and verify basic operations work
    SandboxContext ctx{
        .app_id = "fuzzer.verify",
        .app_path = ".",
        .permissions = {},
        .is_system_app = false
    };

    LuaSandbox sandbox(ctx);

    // Test basic arithmetic
    if (!sandbox.LoadString("return 1 + 1", "verify1")) {
        return false;
    }

    // Test string operations
    if (!sandbox.LoadString("return string.len('test')", "verify2")) {
        return false;
    }

    // Test table operations
    if (!sandbox.LoadString("local t = {1,2,3}; return #t", "verify3")) {
        return false;
    }

    // Verify dangerous functions are still blocked
    if (sandbox.LoadString("return os.execute", "verify_os")) {
        return false;  // os.execute should not exist
    }

    if (sandbox.LoadString("return io.open", "verify_io")) {
        return false;  // io.open should not exist
    }

    return true;
}

FuzzResult LuaFuzzer::Run(size_t iterations) {
    FuzzResult result;
    result.iterations = iterations;

    for (size_t i = 0; i < iterations; i++) {
        m_total_runs++;

        // Generate random code
        std::string code = GenerateRandomCode();

        // Create sandbox with limits
        SandboxContext ctx{
            .app_id = "fuzzer.test",
            .app_path = ".",
            .permissions = {},
            .is_system_app = false
        };

        SandboxLimits limits;
        limits.memory_bytes = 10 * 1024 * 1024;      // 10MB limit
        limits.instructions_per_call = 100000;       // 100k instruction limit

        try {
            LuaSandbox sandbox(ctx, limits);

            // Execute
            bool ok = sandbox.LoadString(code, "fuzz_" + std::to_string(i));
            if (!ok) {
                m_errors_caught++;
                result.errors_caught++;
                // This is expected - random code often has errors
            }
        } catch (const std::exception& e) {
            // Caught exception - not a crash, but unexpected
            m_errors_caught++;
            result.errors_caught++;
            result.error = std::string("Exception: ") + e.what();
        } catch (...) {
            // Unknown exception - this is bad
            m_crashes++;
            result.crashed = true;
            result.error = "Unknown exception caught";
            return result;
        }
    }

    // Verify sandbox is still working correctly
    result.sandbox_intact = VerifySandboxIntegrity();

    return result;
}

} // namespace mosis
