import pytest
from pathlib import Path
import os
import sys

# Add project root to sys.path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from src.mcp_client import ts_cpp_get_skeleton, ts_cpp_get_code_outline, ts_cpp_get_definition, ts_cpp_update_definition

def test_ts_cpp_get_skeleton(tmp_path):
 cpp_code = """#include <iostream>
#include <vector>

template<typename T>
class Box {
public:
 Box(T val) : value(val) {}
 T getValue() {
  return value;
 }
private:
 T value;
};

void globalFunc() {
 std::cout << "Global" << std::endl;
}
"""
 cpp_file = tmp_path / "test.cpp"
 cpp_file.write_text(cpp_code)
 
 from src import mcp_client
 original_resolve = mcp_client._resolve_and_check
 mcp_client._resolve_and_check = lambda path: (Path(path), None)
 
 try:
  skeleton = ts_cpp_get_skeleton(str(cpp_file))
  assert "class Box" in skeleton
  assert "Box(T val) { ... }" in skeleton
  assert "T getValue() { ... }" in skeleton
  assert "void globalFunc() { ... }" in skeleton
  assert "std::cout" not in skeleton
 finally:
  mcp_client._resolve_and_check = original_resolve

def test_ts_cpp_get_code_outline(tmp_path):
 cpp_code = """
class MyClass {
 void method1() {
 }
};

template<typename T>
void templateFunc(T t) {
}
"""
 cpp_file = tmp_path / "test.cpp"
 cpp_file.write_text(cpp_code)
 
 from src import mcp_client
 original_resolve = mcp_client._resolve_and_check
 mcp_client._resolve_and_check = lambda path: (Path(path), None)
 
 try:
  outline = ts_cpp_get_code_outline(str(cpp_file))
  assert "[Class] MyClass (Lines 2-5)" in outline
  assert "[Method] method1 (Lines 3-4)" in outline
  assert "[Func] templateFunc (Lines 8-9)" in outline
 finally:
  mcp_client._resolve_and_check = original_resolve

def test_exhaustive_cpp_samples():
 base_dir = Path(__file__).parent / "assets" / "cpp_samples"
 files_to_test = {
  "base_component.h": [
   "BaseComponent", 
   "BaseComponent::Config", 
   "BaseComponent::Config::Metadata"
  ],
  "complex_template.h": [
   "MultiBuffer", 
   "MultiBuffer::SetData", 
   "MultiBuffer::Get", 
   "TypeTraits", 
   "Container::Wrapper"
  ],
  "component_registry.h": [
   "ComponentRegistry", 
   "ComponentRegistry::Iterator", 
   "ComponentRegistry::Iterator::GetType",
   "ComponentRegistry::Instance"
  ],
  "component_registry.cpp": [
   "ComponentRegistry::Instance", 
   "ComponentRegistry::Register", 
   "ComponentRegistry::Create"
  ]
 }

 from src import mcp_client
 original_resolve = mcp_client._resolve_and_check
 mcp_client._resolve_and_check = lambda path: (Path(path), None)

 try:
  for filename, symbols in files_to_test.items():
   path = base_dir / filename
   assert path.exists(), f"{path} does not exist"
   
   # 1. Verify skeleton executes without errors
   skeleton = ts_cpp_get_skeleton(str(path))
   assert skeleton and "ERROR" not in skeleton
   
   # 2. Verify code outline executes without errors
   outline = ts_cpp_get_code_outline(str(path))
   assert outline and "ERROR" not in outline
   
   # 3. Verify specific complex symbols can be retrieved via ts_cpp_get_definition
   for symbol in symbols:
    definition = ts_cpp_get_definition(str(path), symbol)
    assert definition and "ERROR" not in definition, f"Failed to get definition for {symbol} in {filename}: {definition}"
    # Basic check that the symbol name is in the definition (allowing for namespaces)
    simple_name = symbol.split("::")[-1]
    assert simple_name in definition
 finally:
  mcp_client._resolve_and_check = original_resolve

def test_ts_cpp_update_definition(tmp_path):
 asset_path = Path(__file__).parent / "assets" / "cpp_samples" / "component_registry.cpp"
 cpp_content = asset_path.read_text(encoding="utf-8")
 cpp_file = tmp_path / "component_registry.cpp"
 cpp_file.write_text(cpp_content, encoding="utf-8")

 from src import mcp_client
 original_resolve = mcp_client._resolve_and_check
 mcp_client._resolve_and_check = lambda path: (Path(path), None)

 try:
  new_register_body = """void ComponentRegistry::Register(const std::string& type, ComponentCreator creator) {
 // Updated body
 std::cout << "DEBUG: Registering " << type << std::endl;
 m_creators[type] = creator;
 if (type == "secret") {
  std::cout << "Special component" << std::endl;
 }
}"""
  result = ts_cpp_update_definition(str(cpp_file), "ComponentRegistry::Register", new_register_body)
  assert "Successfully updated" in result
  
  updated_content = cpp_file.read_text(encoding="utf-8")
  assert "// Updated body" in updated_content
  assert "DEBUG: Registering" in updated_content
  
  # Verify it still parses by getting definition again
  definition = ts_cpp_get_definition(str(cpp_file), "ComponentRegistry::Register")
  assert "// Updated body" in definition
  assert "DEBUG: Registering" in definition
 finally:
  mcp_client._resolve_and_check = original_resolve

def test_exhaustive_gencpp_samples():
 base_dir = Path(__file__).parent / "assets" / "gencpp_samples"
 files_to_test = {
  "ast.hpp": [
   "AST", 
   "AST_Body", 
   "AST_Class", 
   "AST_Fn", 
   "AST_Struct"
  ],
  "parser.cpp": [
   "parser_push", 
   "parser_pop", 
   "parser_to_strbuilder", 
   "lex__eat"
  ],
  "types.hpp": [
   "LogLevel", 
   "loglevel_to_str", 
   "AccessSpec", 
   "access_spec_to_str", 
   "ModuleFlag"
  ]
 }

 from src import mcp_client
 original_resolve = mcp_client._resolve_and_check
 mcp_client._resolve_and_check = lambda path: (Path(path), None)

 try:
  for filename, symbols in files_to_test.items():
   path = base_dir / filename
   assert path.exists(), f"{path} does not exist"
   
   # 1. Verify skeleton executes without errors
   skeleton = ts_cpp_get_skeleton(str(path))
   assert skeleton and not skeleton.startswith("ERROR")
   
   # 2. Verify code outline executes without errors
   outline = ts_cpp_get_code_outline(str(path))
   assert outline and not outline.startswith("ERROR")
   
   # 3. Verify specific complex symbols can be retrieved via ts_cpp_get_definition
   for symbol in symbols:
    definition = ts_cpp_get_definition(str(path), symbol)
    assert definition and not definition.startswith("ERROR"), f"Failed for {symbol} in {filename}: {definition}"
    simple_name = symbol.split("::")[-1]
    assert simple_name in definition
 finally:
  mcp_client._resolve_and_check = original_resolve

def test_ts_cpp_update_definition_gencpp(tmp_path):
 asset_path = Path(__file__).parent / "assets" / "gencpp_samples" / "parser.cpp"
 cpp_content = asset_path.read_text(encoding="utf-8")
 cpp_file = tmp_path / "parser.cpp"
 cpp_file.write_text(cpp_content, encoding="utf-8")

 from src import mcp_client
 original_resolve = mcp_client._resolve_and_check
 mcp_client._resolve_and_check = lambda path: (Path(path), None)

 try:
  # Update parser_pop
  new_pop_body = """void parser_pop(ParseContext* ctx)
{
#if 0 && GEN_BUILD_DEBUG
 log_fmt("\\tPopping parser: %.*s\\n", Scope->ProcName.Len, Scope->ProcName.Ptr );
#endif
 // HELLO FROM TEST
 ctx->scope = ctx->scope->prev;
}"""
  result = ts_cpp_update_definition(str(cpp_file), "parser_pop", new_pop_body)
  assert "Successfully updated" in result
  
  updated_content = cpp_file.read_text(encoding="utf-8")
  assert "// HELLO FROM TEST" in updated_content
  
  # Verify retrieval
  definition = ts_cpp_get_definition(str(cpp_file), "parser_pop")
  assert "// HELLO FROM TEST" in definition
 finally:
  mcp_client._resolve_and_check = original_resolve
