#include "lua/wherigo_completion.h"

extern "C" {
#include <lua.h>
#include <lauxlib.h>
}

#include <wx/log.h>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>

namespace wherigo {

std::string WherigoCompletion::escapeXML(const std::string& str) {
  std::string result;
  for (char c : str) {
    switch (c) {
      case '&': result += "&amp;"; break;
      case '<': result += "&lt;"; break;
      case '>': result += "&gt;"; break;
      case '"': result += "&quot;"; break;
      case '\'': result += "&apos;"; break;
      default: result += c;
    }
  }
  return result;
}

std::string WherigoCompletion::formatTime(time_t t) {
  struct tm* timeinfo = localtime(&t);
  char buffer[30];
  strftime(buffer, sizeof(buffer), "%Y-%m-%dT%H:%M:%S", timeinfo);
  return std::string(buffer);
}

std::string WherigoCompletion::generateCompletionCode(const CompletionData& data) {
  // Generate a simple hash based on cartridge GUID + player + completion time
  std::stringstream ss;
  ss << data.cartridgeGUID << data.playerName << data.endTime;
  std::string input = ss.str();

  // Simple hash (for real use, use proper crypto hash like SHA256)
  unsigned long hash = 5381;
  for (char c : input) {
    hash = ((hash << 5) + hash) + c;
  }

  char hashStr[20];
  snprintf(hashStr, sizeof(hashStr), "%016lX", hash);
  return std::string(hashStr);
}

CompletionData WherigoCompletion::extractCompletionData(lua_State* L, const std::string& playerName) {
  CompletionData data;
  data.playerName = playerName;
  data.startTime = time(nullptr); // Default to now
  data.endTime = time(nullptr);
  data.duration = 0;

  // Get cartridge info
  lua_getglobal(L, "_G");
  lua_pushnil(L);

  while (lua_next(L, -2) != 0) {
    if (lua_istable(L, -1)) {
      lua_getfield(L, -1, "ClassName");
      if (lua_isstring(L, -1)) {
        std::string className = lua_tostring(L, -1);

        if (className == "ZCartridge") {
          lua_pop(L, 1); // pop ClassName

          lua_getfield(L, -1, "Name");
          if (lua_isstring(L, -1)) {
            data.cartridgeName = lua_tostring(L, -1);
          }
          lua_pop(L, 1);

          lua_getfield(L, -1, "Id");
          if (lua_isstring(L, -1)) {
            data.cartridgeGUID = lua_tostring(L, -1);
          }
          lua_pop(L, 1);
        }
        else if (className == "ZTask") {
          lua_pop(L, 1); // pop ClassName

          CompletionData::TaskCompletion task;

          lua_getfield(L, -1, "Name");
          if (lua_isstring(L, -1)) {
            task.name = lua_tostring(L, -1);
          }
          lua_pop(L, 1);

          lua_getfield(L, -1, "Complete");
          task.completed = lua_toboolean(L, -1);
          lua_pop(L, 1);

          task.completionTime = time(nullptr);
          data.tasks.push_back(task);
        }
        else if (className == "Zone") {
          lua_pop(L, 1); // pop ClassName

          lua_getfield(L, -1, "Active");
          bool active = lua_toboolean(L, -1);
          lua_pop(L, 1);

          if (active) {
            CompletionData::ZoneVisited zone;

            lua_getfield(L, -1, "Name");
            if (lua_isstring(L, -1)) {
              zone.name = lua_tostring(L, -1);
            }
            lua_pop(L, 1);

            zone.visitTime = time(nullptr);
            data.zones.push_back(zone);
          }
        }
        else {
          lua_pop(L, 1); // pop ClassName
        }
      } else {
        lua_pop(L, 1); // pop ClassName
      }
    }
    lua_pop(L, 1); // pop value
  }
  lua_pop(L, 1); // pop _G

  // Get items in player inventory
  lua_getglobal(L, "Player");
  if (lua_istable(L, -1)) {
    // Find all items where Container == Player
    lua_getglobal(L, "_G");
    lua_pushnil(L);

    while (lua_next(L, -2) != 0) {
      if (lua_istable(L, -1)) {
        lua_getfield(L, -1, "ClassName");
        if (lua_isstring(L, -1) && strcmp(lua_tostring(L, -1), "ZItem") == 0) {
          lua_pop(L, 1);

          lua_getfield(L, -1, "Container");
          if (lua_istable(L, -1)) {
            lua_getglobal(L, "Player");
            bool inInventory = lua_rawequal(L, -1, -2);
            lua_pop(L, 2); // pop Player and Container

            if (inInventory) {
              CompletionData::ItemFound item;

              lua_getfield(L, -1, "Name");
              if (lua_isstring(L, -1)) {
                item.name = lua_tostring(L, -1);
              }
              lua_pop(L, 1);

              item.foundTime = time(nullptr);
              data.items.push_back(item);
            }
          } else {
            lua_pop(L, 1);
          }
        } else {
          lua_pop(L, 1);
        }
      }
      lua_pop(L, 1);
    }
    lua_pop(L, 1); // pop _G
  }
  lua_pop(L, 1); // pop Player

  // Generate completion code
  data.completionCode = generateCompletionCode(data);

  return data;
}

bool WherigoCompletion::writeGWLFile(const CompletionData& data, const std::string& filePath) {
  std::ofstream file(filePath);
  if (!file.is_open()) {
    wxLogError("Failed to create completion log: %s", filePath);
    return false;
  }

  // Write XML header
  file << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  file << "<WherigoGameLog xmlns=\"http://www.groundspeak.com/wherigo/1.0\">\n";

  // Cartridge info
  file << "  <Cartridge>\n";
  file << "    <Name>" << escapeXML(data.cartridgeName) << "</Name>\n";
  file << "    <GUID>" << escapeXML(data.cartridgeGUID) << "</GUID>\n";
  file << "  </Cartridge>\n";

  // Player info
  file << "  <Player>\n";
  file << "    <Name>" << escapeXML(data.playerName) << "</Name>\n";
  file << "  </Player>\n";

  // Session info
  file << "  <Session>\n";
  file << "    <StartTime>" << formatTime(data.startTime) << "</StartTime>\n";
  file << "    <EndTime>" << formatTime(data.endTime) << "</EndTime>\n";
  file << "    <Duration>" << data.duration << "</Duration>\n";
  file << "  </Session>\n";

  // Completion status
  bool allTasksComplete = std::all_of(data.tasks.begin(), data.tasks.end(),
                                       [](const CompletionData::TaskCompletion& t) { return t.completed; });
  file << "  <CompletionStatus>" << (allTasksComplete ? "Complete" : "Incomplete") << "</CompletionStatus>\n";

  // Tasks
  if (!data.tasks.empty()) {
    file << "  <Tasks>\n";
    for (const auto& task : data.tasks) {
      file << "    <Task>\n";
      file << "      <Name>" << escapeXML(task.name) << "</Name>\n";
      file << "      <Completed>" << (task.completed ? "true" : "false") << "</Completed>\n";
      if (task.completed) {
        file << "      <CompletionTime>" << formatTime(task.completionTime) << "</CompletionTime>\n";
      }
      file << "    </Task>\n";
    }
    file << "  </Tasks>\n";
  }

  // Items collected
  if (!data.items.empty()) {
    file << "  <ItemsCollected>\n";
    for (const auto& item : data.items) {
      file << "    <Item>\n";
      file << "      <Name>" << escapeXML(item.name) << "</Name>\n";
      file << "      <FoundTime>" << formatTime(item.foundTime) << "</FoundTime>\n";
      file << "    </Item>\n";
    }
    file << "  </ItemsCollected>\n";
  }

  // Zones visited
  if (!data.zones.empty()) {
    file << "  <ZonesVisited>\n";
    for (const auto& zone : data.zones) {
      file << "    <Zone>\n";
      file << "      <Name>" << escapeXML(zone.name) << "</Name>\n";
      file << "      <VisitTime>" << formatTime(zone.visitTime) << "</VisitTime>\n";
      file << "    </Zone>\n";
    }
    file << "  </ZonesVisited>\n";
  }

  // Completion code (signature/verification)
  file << "  <CompletionCode>" << data.completionCode << "</CompletionCode>\n";

  file << "</WherigoGameLog>\n";
  file.close();

  return true;
}

bool WherigoCompletion::generateCompletionLog(lua_State* L, const std::string& filePath,
                                               const std::string& playerName) {
  if (!L) {
    wxLogError("Invalid Lua state");
    return false;
  }

  CompletionData data = extractCompletionData(L, playerName);
  return writeGWLFile(data, filePath);
}

} // namespace wherigo

