#include "test_harness.h"
#include <nlohmann/json.hpp>
#include <fstream>
#include <iomanip>
#include <ctime>

void TestHarness::AddTest(const std::string& name, std::function<bool(std::string&)> func) {
    m_tests.push_back({name, func});
}

std::vector<TestResult> TestHarness::Run(const std::string& filter) {
    std::vector<TestResult> results;

    for (const auto& test : m_tests) {
        // Filter check
        if (!filter.empty() && test.name.find(filter) == std::string::npos) {
            continue;
        }

        TestResult result;
        result.name = test.name;

        std::cout << "Running: " << test.name << "... " << std::flush;

        auto start = std::chrono::steady_clock::now();

        try {
            std::string error;
            result.passed = test.func(error);
            result.error_message = error;
        } catch (const std::exception& e) {
            result.passed = false;
            result.error_message = std::string("Exception: ") + e.what();
        } catch (...) {
            result.passed = false;
            result.error_message = "Unknown exception";
        }

        auto end = std::chrono::steady_clock::now();
        result.duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();

        if (result.passed) {
            std::cout << "PASSED (" << result.duration_ms << "ms)\n";
        } else {
            std::cout << "FAILED\n";
            std::cout << "  Error: " << result.error_message << "\n";
        }

        results.push_back(result);
    }

    return results;
}

void TestHarness::WriteJsonReport(const std::vector<TestResult>& results, const std::string& path) {
    nlohmann::json report;

    // Get timestamp
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    std::stringstream ss;
    ss << std::put_time(std::gmtime(&time), "%Y-%m-%dT%H:%M:%SZ");

    report["name"] = "Lua Sandbox Security Tests";
    report["timestamp"] = ss.str();

    int passed = 0, failed = 0;
    for (const auto& r : results) {
        if (r.passed) passed++;
        else failed++;
    }

    report["summary"]["passed"] = passed;
    report["summary"]["failed"] = failed;
    report["summary"]["total"] = static_cast<int>(results.size());

    nlohmann::json tests = nlohmann::json::array();
    for (const auto& r : results) {
        nlohmann::json t;
        t["name"] = r.name;
        t["status"] = r.passed ? "passed" : "failed";
        t["duration_ms"] = r.duration_ms;
        if (!r.passed && !r.error_message.empty()) {
            t["error"] = r.error_message;
        }
        tests.push_back(t);
    }
    report["tests"] = tests;

    std::ofstream f(path);
    f << report.dump(2);
}

void TestHarness::PrintResults(const std::vector<TestResult>& results) {
    std::cout << "\n";
    std::cout << "========================================\n";
    std::cout << "           TEST RESULTS\n";
    std::cout << "========================================\n\n";

    int passed = 0, failed = 0;
    for (const auto& r : results) {
        if (r.passed) passed++;
        else failed++;
    }

    std::cout << "Total:  " << results.size() << "\n";
    std::cout << "Passed: " << passed << "\n";
    std::cout << "Failed: " << failed << "\n\n";

    if (failed > 0) {
        std::cout << "FAILED TESTS:\n";
        for (const auto& r : results) {
            if (!r.passed) {
                std::cout << "  - " << r.name << "\n";
                std::cout << "    " << r.error_message << "\n";
            }
        }
        std::cout << "\n";
    }

    if (failed == 0) {
        std::cout << "ALL TESTS PASSED!\n";
    } else {
        std::cout << "SOME TESTS FAILED!\n";
    }

    std::cout << "========================================\n";
}
