#ifndef YAZE_APP_TEST_INTEGRATED_TEST_SUITE_H
#define YAZE_APP_TEST_INTEGRATED_TEST_SUITE_H

#include <chrono>
#include <memory>
#include <vector>
#include <filesystem>

#include "absl/strings/str_format.h"
#include "app/test/test_manager.h"
#include "app/gfx/arena.h"
#include "app/rom.h"

#ifdef YAZE_ENABLE_GTEST
#include <gtest/gtest.h>
#endif

namespace yaze {
namespace test {

// Integrated test suite that runs actual unit tests within the main application
class IntegratedTestSuite : public TestSuite {
 public:
  IntegratedTestSuite() = default;
  ~IntegratedTestSuite() override = default;

  std::string GetName() const override { return "Integrated Unit Tests"; }
  TestCategory GetCategory() const override { return TestCategory::kUnit; }

  absl::Status RunTests(TestResults& results) override {
    // Run Arena tests
    RunArenaIntegrityTest(results);
    RunArenaResourceManagementTest(results);
    
    // Run ROM tests  
    RunRomBasicTest(results);
    
    // Run Graphics tests
    RunGraphicsValidationTest(results);
    
    return absl::OkStatus();
  }

  void DrawConfiguration() override {
    ImGui::Text("Integrated Test Configuration");
    ImGui::Checkbox("Test Arena operations", &test_arena_);
    ImGui::Checkbox("Test ROM loading", &test_rom_);
    ImGui::Checkbox("Test graphics pipeline", &test_graphics_);
    
    if (ImGui::CollapsingHeader("ROM Test Settings")) {
      ImGui::InputText("Test ROM Path", test_rom_path_, sizeof(test_rom_path_));
      ImGui::Checkbox("Skip ROM tests if file missing", &skip_missing_rom_);
    }
  }

 private:
  void RunArenaIntegrityTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Arena_Integrity_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    try {
      auto& arena = gfx::Arena::Get();
      
      // Test basic Arena functionality
      size_t initial_textures = arena.GetTextureCount();
      size_t initial_surfaces = arena.GetSurfaceCount();
      
      // Verify Arena is properly initialized
      if (initial_textures >= 0 && initial_surfaces >= 0) {
        result.status = TestStatus::kPassed;
        result.error_message = absl::StrFormat(
            "Arena initialized: %zu textures, %zu surfaces", 
            initial_textures, initial_surfaces);
      } else {
        result.status = TestStatus::kFailed;
        result.error_message = "Arena returned invalid resource counts";
      }
      
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Arena integrity test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunArenaResourceManagementTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Arena_Resource_Management_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    try {
      auto& arena = gfx::Arena::Get();
      
      size_t before_textures = arena.GetTextureCount();
      size_t before_surfaces = arena.GetSurfaceCount();
      
      // Test surface allocation (without renderer for now)
      // In a real test environment, we'd create a test renderer
      
      size_t after_textures = arena.GetTextureCount();
      size_t after_surfaces = arena.GetSurfaceCount();
      
      // Verify resource tracking works
      if (after_textures >= before_textures && after_surfaces >= before_surfaces) {
        result.status = TestStatus::kPassed;
        result.error_message = absl::StrFormat(
            "Resource tracking working: %zu→%zu textures, %zu→%zu surfaces",
            before_textures, after_textures, before_surfaces, after_surfaces);
      } else {
        result.status = TestStatus::kFailed;
        result.error_message = "Resource counting inconsistent";
      }
      
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Resource management test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunRomBasicTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "ROM_Basic_Operations_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    if (!test_rom_) {
      result.status = TestStatus::kSkipped;
      result.error_message = "ROM testing disabled in configuration";
    } else {
      try {
        // First try to use currently loaded ROM from editor
        Rom* current_rom = TestManager::Get().GetCurrentRom();
        
        if (current_rom && current_rom->is_loaded()) {
          // Test with currently loaded ROM
          result.status = TestStatus::kPassed;
          result.error_message = absl::StrFormat(
              "Current ROM validated: %s (%zu bytes)", 
              current_rom->title().c_str(), current_rom->size());
        } else {
          // Fallback to loading ROM file
          Rom test_rom;
          std::string rom_path = test_rom_path_;
          if (rom_path.empty()) {
            rom_path = "zelda3.sfc";
          }
          
          if (std::filesystem::exists(rom_path)) {
            auto status = test_rom.LoadFromFile(rom_path);
            if (status.ok()) {
              result.status = TestStatus::kPassed;
              result.error_message = absl::StrFormat(
                  "ROM loaded from file: %s (%zu bytes)", 
                  test_rom.title().c_str(), test_rom.size());
            } else {
              result.status = TestStatus::kFailed;
              result.error_message = "ROM loading failed: " + std::string(status.message());
            }
          } else if (skip_missing_rom_) {
            result.status = TestStatus::kSkipped;
            result.error_message = "No current ROM and file not found: " + rom_path;
          } else {
            result.status = TestStatus::kFailed;
            result.error_message = "No current ROM and required file not found: " + rom_path;
          }
        }
        
      } catch (const std::exception& e) {
        result.status = TestStatus::kFailed;
        result.error_message = "ROM test failed: " + std::string(e.what());
      }
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunGraphicsValidationTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Graphics_Pipeline_Validation_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    if (!test_graphics_) {
      result.status = TestStatus::kSkipped;
      result.error_message = "Graphics testing disabled in configuration";
    } else {
      try {
        // Test basic graphics pipeline components
        auto& arena = gfx::Arena::Get();
        
        // Test that graphics sheets can be accessed
        auto& gfx_sheets = arena.gfx_sheets();
        
        // Basic validation
        if (gfx_sheets.size() == 223) {
          result.status = TestStatus::kPassed;
          result.error_message = absl::StrFormat(
              "Graphics pipeline validated: %zu sheets available", 
              gfx_sheets.size());
        } else {
          result.status = TestStatus::kFailed;
          result.error_message = absl::StrFormat(
              "Graphics sheets count mismatch: expected 223, got %zu", 
              gfx_sheets.size());
        }
        
      } catch (const std::exception& e) {
        result.status = TestStatus::kFailed;
        result.error_message = "Graphics validation failed: " + std::string(e.what());
      }
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  // Configuration
  bool test_arena_ = true;
  bool test_rom_ = true;
  bool test_graphics_ = true;
  char test_rom_path_[256] = "zelda3.sfc";
  bool skip_missing_rom_ = true;
};

// Performance test suite for monitoring system performance
class PerformanceTestSuite : public TestSuite {
 public:
  PerformanceTestSuite() = default;
  ~PerformanceTestSuite() override = default;

  std::string GetName() const override { return "Performance Tests"; }
  TestCategory GetCategory() const override { return TestCategory::kPerformance; }

  absl::Status RunTests(TestResults& results) override {
    RunFrameRateTest(results);
    RunMemoryUsageTest(results);
    RunResourceLeakTest(results);
    
    return absl::OkStatus();
  }

  void DrawConfiguration() override {
    ImGui::Text("Performance Test Configuration");
    ImGui::InputInt("Sample duration (seconds)", &sample_duration_secs_);
    ImGui::InputFloat("Target FPS", &target_fps_);
    ImGui::InputInt("Max memory MB", &max_memory_mb_);
  }

 private:
  void RunFrameRateTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Frame_Rate_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    try {
      // Sample current frame rate
      float current_fps = ImGui::GetIO().Framerate;
      
      if (current_fps >= target_fps_) {
        result.status = TestStatus::kPassed;
        result.error_message = absl::StrFormat(
            "Frame rate acceptable: %.1f FPS (target: %.1f)", 
            current_fps, target_fps_);
      } else {
        result.status = TestStatus::kFailed;
        result.error_message = absl::StrFormat(
            "Frame rate below target: %.1f FPS (target: %.1f)", 
            current_fps, target_fps_);
      }
      
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Frame rate test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunMemoryUsageTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Memory_Usage_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    try {
      auto& arena = gfx::Arena::Get();
      
      // Estimate memory usage based on resource counts
      size_t texture_count = arena.GetTextureCount();
      size_t surface_count = arena.GetSurfaceCount();
      
      // Rough estimation: each texture/surface ~1KB average
      size_t estimated_memory_kb = (texture_count + surface_count);
      size_t estimated_memory_mb = estimated_memory_kb / 1024;
      
      if (static_cast<int>(estimated_memory_mb) <= max_memory_mb_) {
        result.status = TestStatus::kPassed;
        result.error_message = absl::StrFormat(
            "Memory usage acceptable: ~%zu MB (%zu textures, %zu surfaces)", 
            estimated_memory_mb, texture_count, surface_count);
      } else {
        result.status = TestStatus::kFailed;
        result.error_message = absl::StrFormat(
            "Memory usage high: ~%zu MB (limit: %d MB)", 
            estimated_memory_mb, max_memory_mb_);
      }
      
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Memory usage test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunResourceLeakTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Resource_Leak_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    try {
      auto& arena = gfx::Arena::Get();
      
      // Get baseline resource counts
      size_t baseline_textures = arena.GetTextureCount();
      size_t baseline_surfaces = arena.GetSurfaceCount();
      
      // Simulate some operations (this would be more comprehensive with actual workload)
      // For now, just verify resource counts remain stable
      
      size_t final_textures = arena.GetTextureCount();
      size_t final_surfaces = arena.GetSurfaceCount();
      
      // Check for unexpected resource growth
      size_t texture_diff = final_textures > baseline_textures ? 
                           final_textures - baseline_textures : 0;
      size_t surface_diff = final_surfaces > baseline_surfaces ?
                           final_surfaces - baseline_surfaces : 0;
      
      if (texture_diff == 0 && surface_diff == 0) {
        result.status = TestStatus::kPassed;
        result.error_message = "No resource leaks detected";
      } else if (texture_diff < 10 && surface_diff < 10) {
        result.status = TestStatus::kPassed;
        result.error_message = absl::StrFormat(
            "Minor resource growth: +%zu textures, +%zu surfaces (acceptable)", 
            texture_diff, surface_diff);
      } else {
        result.status = TestStatus::kFailed;
        result.error_message = absl::StrFormat(
            "Potential resource leak: +%zu textures, +%zu surfaces", 
            texture_diff, surface_diff);
      }
      
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Resource leak test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  // Configuration
  bool test_arena_ = true;
  bool test_rom_ = true;
  bool test_graphics_ = true;
  int sample_duration_secs_ = 5;
  float target_fps_ = 30.0f;
  int max_memory_mb_ = 100;
  char test_rom_path_[256] = "zelda3.sfc";
  bool skip_missing_rom_ = true;
};

// UI Testing suite that integrates with ImGui Test Engine
class UITestSuite : public TestSuite {
 public:
  UITestSuite() = default;
  ~UITestSuite() override = default;

  std::string GetName() const override { return "UI Interaction Tests"; }
  TestCategory GetCategory() const override { return TestCategory::kUI; }

  absl::Status RunTests(TestResults& results) override {
#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
    RunMenuInteractionTest(results);
    RunDialogTest(results);
    RunTestDashboardTest(results);
#else
    TestResult result;
    result.name = "UI_Tests_Disabled";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.status = TestStatus::kSkipped;
    result.error_message = "ImGui Test Engine not available in this build";
    result.duration = std::chrono::milliseconds{0};
    result.timestamp = std::chrono::steady_clock::now();
    results.AddResult(result);
#endif
    
    return absl::OkStatus();
  }

  void DrawConfiguration() override {
    ImGui::Text("UI Test Configuration");
#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
    ImGui::Checkbox("Test menu interactions", &test_menus_);
    ImGui::Checkbox("Test dialog workflows", &test_dialogs_);
    ImGui::Checkbox("Test dashboard UI", &test_dashboard_);
    ImGui::InputFloat("UI interaction delay (ms)", &interaction_delay_ms_);
#else
    ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), 
                      "UI tests not available - ImGui Test Engine disabled");
#endif
  }

 private:
#ifdef YAZE_ENABLE_IMGUI_TEST_ENGINE
  void RunMenuInteractionTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Menu_Interaction_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    try {
      auto* engine = TestManager::Get().GetUITestEngine();
      if (engine) {
        // This would register and run actual UI tests
        // For now, just verify the test engine is available
        result.status = TestStatus::kPassed;
        result.error_message = "UI test engine available for menu testing";
      } else {
        result.status = TestStatus::kFailed;
        result.error_message = "UI test engine not available";
      }
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Menu interaction test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunDialogTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Dialog_Workflow_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    // Placeholder for dialog testing
    result.status = TestStatus::kSkipped;
    result.error_message = "Dialog testing not yet implemented";
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  void RunTestDashboardTest(TestResults& results) {
    auto start_time = std::chrono::steady_clock::now();
    
    TestResult result;
    result.name = "Test_Dashboard_UI_Test";
    result.suite_name = GetName();
    result.category = GetCategory();
    result.timestamp = start_time;
    
    // Test that the dashboard can be accessed and drawn
    try {
      // The fact that we're running this test means the dashboard is working
      result.status = TestStatus::kPassed;
      result.error_message = "Test dashboard UI functioning correctly";
    } catch (const std::exception& e) {
      result.status = TestStatus::kFailed;
      result.error_message = "Dashboard test failed: " + std::string(e.what());
    }
    
    auto end_time = std::chrono::steady_clock::now();
    result.duration = std::chrono::duration_cast<std::chrono::milliseconds>(
        end_time - start_time);
    
    results.AddResult(result);
  }
  
  bool test_menus_ = true;
  bool test_dialogs_ = true;
  bool test_dashboard_ = true;
  float interaction_delay_ms_ = 100.0f;
#endif
};

}  // namespace test
}  // namespace yaze

#endif  // YAZE_APP_TEST_INTEGRATED_TEST_SUITE_H
