// Step 62 TDD Test: Context API
//
// Tests the agent context API:
// 1. getInScopeSymbols returns variables/functions/parameters visible at a position
// 2. getCallHierarchy returns callers and callees
// 3. getDependencyGraph returns data flow dependencies
// 4. Scope respects nesting (inner scope sees outer, not vice versa)

#include <iostream>
#include <string>
#include <cassert>
#include <vector>
#include <algorithm>
#include "ast/ASTNode.h"
#include "ast/Module.h"
#include "ast/Function.h"
#include "ast/Variable.h"
#include "ast/Parameter.h"
#include "ast/Statement.h"
#include "ast/Expression.h"
#include "ContextAPI.h"

static bool hasSymbol(const std::vector<ContextAPI::Symbol>& symbols, const std::string& name) {
    return std::any_of(symbols.begin(), symbols.end(),
                       [&](const ContextAPI::Symbol& s) { return s.name == name; });
}

int main() {
    int passed = 0;
    int failed = 0;

    // Build test AST:
    // Module TestModule:
    //   Variable: globalVar
    //   Function add(x, y):
    //     Variable: localSum
    //     Return: localSum
    //   Function main():
    //     Variable: result
    //     ExpressionStatement: add(1, 2)  [FunctionCall]
    Module mod("m1", "TestModule", "python");

    Variable* globalVar = new Variable("gv1", "globalVar");
    mod.addChild("variables", globalVar);

    Function* addFn = new Function("f1", "add");
    Parameter* px = new Parameter("p1", "x");
    Parameter* py = new Parameter("p2", "y");
    addFn->addChild("parameters", px);
    addFn->addChild("parameters", py);

    Variable* localSum = new Variable("v1", "localSum");
    addFn->addChild("body", localSum);

    Return* ret = new Return();
    ret->id = "r1";
    VariableReference* retRef = new VariableReference("vr1", "localSum");
    ret->setChild("value", retRef);
    addFn->addChild("body", ret);

    mod.addChild("functions", addFn);

    Function* mainFn = new Function("f2", "main");
    Variable* result = new Variable("v2", "result");
    mainFn->addChild("body", result);

    ExpressionStatement* callStmt = new ExpressionStatement();
    callStmt->id = "es1";
    FunctionCall* call = new FunctionCall();
    call->id = "fc1";
    call->functionName = "add";
    IntegerLiteral* arg1 = new IntegerLiteral("i1", 1);
    IntegerLiteral* arg2 = new IntegerLiteral("i2", 2);
    call->addChild("arguments", arg1);
    call->addChild("arguments", arg2);
    callStmt->setChild("expression", call);
    mainFn->addChild("body", callStmt);

    mod.addChild("functions", mainFn);

    ContextAPI api;
    api.setRoot(&mod);

    // --- Test 1: Symbols in scope inside add function body ---
    {
        auto symbols = api.getInScopeSymbols("v1");  // localSum inside add()
        assert(hasSymbol(symbols, "x") && "Should see parameter x");
        assert(hasSymbol(symbols, "y") && "Should see parameter y");
        assert(hasSymbol(symbols, "localSum") && "Should see local variable");
        assert(hasSymbol(symbols, "globalVar") && "Should see module-level variable");
        assert(hasSymbol(symbols, "add") && "Should see sibling function");

        std::cout << "Test 1 PASS: Inner scope sees params, locals, globals, and functions" << std::endl;
        ++passed;
    }

    // --- Test 2: Module scope doesn't see function locals ---
    {
        auto symbols = api.getInScopeSymbols("m1");  // Module level
        assert(hasSymbol(symbols, "globalVar") && "Module should see its own variables");
        assert(hasSymbol(symbols, "add") && "Module should see its functions");
        assert(hasSymbol(symbols, "main") && "Module should see its functions");
        assert(!hasSymbol(symbols, "localSum") && "Module should NOT see function locals");
        assert(!hasSymbol(symbols, "x") && "Module should NOT see function parameters");

        std::cout << "Test 2 PASS: Module scope doesn't see function internals" << std::endl;
        ++passed;
    }

    // --- Test 3: main() scope sees its locals but not add() locals ---
    {
        auto symbols = api.getInScopeSymbols("v2");  // result inside main()
        assert(hasSymbol(symbols, "result") && "Should see own local");
        assert(hasSymbol(symbols, "globalVar") && "Should see module variable");
        assert(hasSymbol(symbols, "add") && "Should see sibling function");
        assert(!hasSymbol(symbols, "localSum") && "Should NOT see add()'s locals");
        assert(!hasSymbol(symbols, "x") && "Should NOT see add()'s parameters");

        std::cout << "Test 3 PASS: main() doesn't see add()'s locals/params" << std::endl;
        ++passed;
    }

    // --- Test 4: Call hierarchy for add() ---
    {
        auto callInfo = api.getCallHierarchy("f1");  // add function
        assert(callInfo.functionName == "add" && "Should be about 'add'");
        // main() calls add(), so add should have main as a caller
        assert(!callInfo.callerIds.empty() && "add() should have callers");
        bool mainCalls = std::any_of(callInfo.callerIds.begin(), callInfo.callerIds.end(),
                                      [](const std::string& id) { return id == "f2"; });
        assert(mainCalls && "main (f2) should be a caller of add");

        std::cout << "Test 4 PASS: Call hierarchy shows main() calls add()" << std::endl;
        ++passed;
    }

    // --- Test 5: Call hierarchy for main() ---
    {
        auto callInfo = api.getCallHierarchy("f2");  // main function
        assert(callInfo.functionName == "main" && "Should be about 'main'");
        // main() calls add()
        bool callsAdd = std::any_of(callInfo.calleeIds.begin(), callInfo.calleeIds.end(),
                                     [](const std::string& id) { return id == "f1"; });
        assert(callsAdd && "main should call add (f1)");

        std::cout << "Test 5 PASS: Call hierarchy shows main() calls add()" << std::endl;
        ++passed;
    }

    // --- Test 6: Dependency graph for return value ---
    {
        auto deps = api.getDependencyGraph("r1");  // Return node
        // The return depends on localSum (vr1 references it)
        assert(!deps.empty() && "Return should have dependencies");

        std::cout << "Test 6 PASS: Dependency graph returns dependencies" << std::endl;
        ++passed;
    }

    // Cleanup
    delete arg2;
    delete arg1;
    delete call;
    delete callStmt;
    delete result;
    delete mainFn;
    delete retRef;
    delete ret;
    delete localSum;
    delete py;
    delete px;
    delete addFn;
    delete globalVar;

    // --- Summary ---
    std::cout << "\n=== Step 62 Results: " << passed << " passed, " << failed << " failed ===" << std::endl;
    return failed > 0 ? 1 : 0;
}
