#include <iostream>
#include <OgreTerrainGroup.h>
#include "Components.h"
#include "GameData.h"
#include "EditorGizmoModule.h"
#include "TerrainModule.h"
#include "town.h"
#include "property_editor.h"
#include "property_editor_color_rect.h"
#include "property_editor_npc.h"

namespace ECS
{
namespace Items
{
template <typename T> struct registerPropertyEditor {
    registerPropertyEditor(std::vector<PropertyEditor> &reg)
    {
        static T regdata;
        reg.push_back(regdata);
    }
};
static std::vector<PropertyEditor> propertyEditors;
struct StringPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return j[ptr].is_string();
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        static char buffer[260];
        strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
            sizeof(buffer));

        ImGui::TableSetColumnIndex(0);
        ImGui::Text("%s", ptr.back().c_str());
        ImGui::TableSetColumnIndex(1);
        ImGui::InputText("##value", buffer, sizeof(buffer));

        if (ImGui::IsItemDeactivatedAfterEdit()) {
            j[ptr] = buffer;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
        }
    }
    StringPropertyEditor()
        : DPropertyEditor(EditString, "String Editor")
    {
    }
};
static registerPropertyEditor<StringPropertyEditor>
    stringPropertyEditor(propertyEditors);
// EditLuaScript
struct LuaScriptPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return itemName == "cellScript" && j[ptr].is_string();
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        static char buffer[16384];
        strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
            sizeof(buffer));

        ImGui::TableSetColumnIndex(0);
        ImGui::Text("%s", ptr.back().c_str());
        ImGui::TableSetColumnIndex(1);
        ImGui::PushItemWidth(-FLT_MIN);
        ImGui::BeginChild("##multilineEdit", ImVec2(0, 0), false,
                  ImGuiWindowFlags_HorizontalScrollbar);
        ImGui::InputTextMultiline("##value", buffer, sizeof(buffer),
                      ImVec2(900, -FLT_MIN),
                      ImGuiInputTextFlags_AllowTabInput);

        if (ImGui::IsItemDeactivatedAfterEdit()) {
            j[ptr] = buffer;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
        }
        ImGui::EndChild();
        ImGui::PopItemWidth();
        if (ImGui::SmallButton("Update script...")) {
            j[ptr] = buffer;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
        }
        if (ImGui::SmallButton("Run Lua script...")) {
            j[ptr] = buffer;
            nlohmann::json &lot = j[ptr.parent_pointer()];
            Items::runSingleLotScript(entity, lot);
        }
    }
    LuaScriptPropertyEditor()
        : DPropertyEditor(EditLuaScript, "Lua Script Editor")
    {
    }
};
static registerPropertyEditor<LuaScriptPropertyEditor>
    luaScriptPropertyEditor(propertyEditors);
// Multiline string editor
struct MultilineStringPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return itemName != "cellScript" && j[ptr].is_string() &&
               (j[ptr].get<std::string>().find("\n") !=
                std::string::npos ||
            j[ptr].get<std::string>().length() >= 100);
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        static char buffer[4096];
        strncpy(buffer, j[ptr].get<Ogre::String>().c_str(),
            sizeof(buffer));

        ImGui::TableSetColumnIndex(0);
        ImGui::Text("%s", ptr.back().c_str());
        ImGui::TableSetColumnIndex(1);
        ImGui::InputTextMultiline("##value", buffer, sizeof(buffer));

        if (ImGui::IsItemDeactivatedAfterEdit()) {
            j[ptr] = buffer;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
            StaticGeometryModule::updateItemGeometry(entity);
        }
        if (ImGui::SmallButton("Update value")) {
            j[ptr] = buffer;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
            StaticGeometryModule::updateItemGeometry(entity);
        }
    }
    MultilineStringPropertyEditor()
        : DPropertyEditor(EditMultilineString,
                  "Multiline String Editor")
    {
    }
};
static registerPropertyEditor<MultilineStringPropertyEditor>
    multilineStringPropertyEditor(propertyEditors);
// Float editor
struct FloatPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return j[ptr].is_number_float();
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        ImGui::TableSetColumnIndex(0);
        ImGui::Text("%s", ptr.back().c_str());
        ImGui::TableSetColumnIndex(1);

        float val = j[ptr].get<float>();
        if (ImGui::InputFloat("##value", &val, 0.001f, 0.01f)) {
            j[ptr] = val;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
            StaticGeometryModule::updateItemGeometry(entity);
        }
    }
    FloatPropertyEditor()
        : DPropertyEditor(EditFloat, "Float Editor")
    {
    }
};
static registerPropertyEditor<FloatPropertyEditor>
    floatPropertyEditor(propertyEditors);
// int editor
struct IntPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return j[ptr].is_number_integer();
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        ImGui::TableSetColumnIndex(0);
        ImGui::Text("%s", ptr.back().c_str());
        ImGui::TableSetColumnIndex(1);

        int val = j[ptr].get<int>();
        if (ImGui::InputInt("##value", &val, 1, 10)) {
            j[ptr] = val;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
            StaticGeometryModule::updateItemGeometry(entity);
        }
    }
    IntPropertyEditor()
        : DPropertyEditor(EditInt, "Int Editor")
    {
    }
};
static registerPropertyEditor<IntPropertyEditor>
    intPropertyEditor(propertyEditors);
// bool editor
struct BoolPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return j[ptr].is_boolean();
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        ImGui::TableSetColumnIndex(0);
        ImGui::Text("%s", ptr.back().c_str());
        ImGui::TableSetColumnIndex(1);

        bool val = j[ptr].get<bool>();
        if (ImGui::Checkbox("##value", &val)) {
            j[ptr] = val;
            StaticGeometryModule::setItemProperties(entity,
                                j.dump());
            StaticGeometryModule::updateItemGeometry(entity);
        }
    }
    BoolPropertyEditor()
        : DPropertyEditor(EditBool, "Bool Editor")
    {
    }
};
static registerPropertyEditor<BoolPropertyEditor>
    boolPropertyEditor(propertyEditors);
static registerPropertyEditor<ColorRectPropertyEditor>
    colorRectPropertyEditor(propertyEditors);
static registerPropertyEditor<PropertyEditorNPC>
    propertyEditorNPC(propertyEditors);
// ViewJson editor (fallback)
struct FallbackPropertyEditor : DPropertyEditor {
    bool _matches(const std::string &itemName,
              const nlohmann::json::json_pointer &ptr,
              const nlohmann::json &j) override
    {
        return true;
    }
    void _render(flecs::entity entity,
             const nlohmann::json::json_pointer &ptr,
             nlohmann::json &j) override
    {
        ImGui::TableSetColumnIndex(1);
        ImGui::TextWrapped("%s", j[ptr].dump(4).c_str());
    }
    FallbackPropertyEditor()
        : DPropertyEditor(ViewJson, "JSON Viewer")
    {
    }
};
static registerPropertyEditor<FallbackPropertyEditor>
    fallbackPropertyEditor(propertyEditors);
struct ItemEditor {
    // itemsEditor.h or at the top of your cpp file
    struct EditorState {
        flecs::entity edited;
        nlohmann::json::json_pointer edited_ptr;
        int edited_type;

        EditorState()
            : edited_type(EditNone)
        {
        }

        void clear()
        {
            edited = flecs::entity();
            edited_ptr = nlohmann::json::json_pointer();
            edited_type = EditNone;
        }
    };
    EditorState state;

    // Helper to find matching editor
    PropertyEditor *findEditor(int editType, const std::string &itemName,
                   const nlohmann::json::json_pointer &ptr,
                   const nlohmann::json &j)
    {
        for (auto &editor : propertyEditors) {
            if (editor.editType == editType &&
                editor.matches(itemName, ptr, j)) {
                return &editor;
            }
        }
        return nullptr;
    }
    // Leaf node detection - keeping your working logic
    bool is_leaf(const std::string &itemName,
             const nlohmann::json::json_pointer &ptr)
    {
        // Direct key matches
        if (itemName == "name" || itemName == "position" ||
            itemName == "orientation" || itemName == "rotation" ||
            itemName == "cells" || itemName == "furniture_cells" ||
            itemName == "roofs" || itemName == "pierPath" ||
            itemName == "cellScript") {
            return true;
        }

        // Parent-based matches
        if (!ptr.empty() && !ptr.parent_pointer().empty()) {
            std::string parent = ptr.parent_pointer().back();
            if (parent == "colorRects" || parent == "npcs" ||
                parent == "centerBuildings") {
                return true;
            }
        }

        return false;
    };
    // Edit type determination
    void set_edit_type(const std::string &itemName,
               const nlohmann::json::json_pointer &ptr,
               const nlohmann::json &j)
    {
        // Try to find by specific rules first
        if (itemName == "name") {
            state.edited_type = EditString;
        } else if (itemName == "position" ||
               itemName == "orientation" ||
               itemName == "rotation" || itemName == "cells" ||
               itemName == "furniture_cells" ||
               itemName == "roofs" || itemName == "pierPath") {
            state.edited_type = ViewJson;
        } else if (itemName == "cellScript") {
            state.edited_type = EditLuaScript;
        } else if (!ptr.empty() && !ptr.parent_pointer().empty()) {
            std::string parent = ptr.parent_pointer().back();
            if (parent == "colorRects") {
                state.edited_type = EditColorRect;
            } else if (parent == "npcs" ||
                   parent == "centerBuildings") {
                state.edited_type = ViewJson;
            } else {
                // Fall back to type-based detection
                if (j[ptr].is_number_float())
                    state.edited_type = EditFloat;
                else if (j[ptr].is_number_integer())
                    state.edited_type = EditInt;
                else if (j[ptr].is_boolean())
                    state.edited_type = EditBool;
                else if (j[ptr].is_string())
                    state.edited_type = EditString;
                else
                    state.edited_type = ViewJson;
            }
        } else {
            // Fall back to type-based detection
            if (j[ptr].is_number_float())
                state.edited_type = EditFloat;
            else if (j[ptr].is_number_integer())
                state.edited_type = EditInt;
            else if (j[ptr].is_boolean())
                state.edited_type = EditBool;
            else if (j[ptr].is_string())
                state.edited_type = EditString;
            else
                state.edited_type = ViewJson;
        }
    };
    void _setCameraSelectedPos(flecs::entity e)
    {
        Ogre::Vector3 cursorPos;
        Ogre::Quaternion cursorOrientation;
        // setCursorSelectedPos(e, cursorPos, cursorOrientation);
        StaticGeometryModule::getItemPositionAndRotation(
            e, cursorPos, cursorOrientation);
        ECS::get<EditorGizmo>().sceneNode->_setDerivedPosition(
            cursorPos);
        ECS::get<EditorGizmo>().sceneNode->_setDerivedOrientation(
            cursorOrientation);
        Ogre::Vector3 cameraPos =
            ECS::get<Camera>().mCameraPivot->_getDerivedPosition();
        Ogre::Vector3 cameraOffset =
            cursorOrientation * Ogre::Vector3::UNIT_Z * 30.0f;
        cameraPos.x = cursorPos.x;
        cameraPos.z = cursorPos.z;
        cameraPos += cameraOffset;
        cameraPos.y =
            TerrainModule::get_height(
                ECS::get<Terrain>().mTerrainGroup, cameraPos) +
            10.0f;
        Ogre::TerrainGroup *tg = ECS::get<Terrain>().mTerrainGroup;
        long x, y;
        tg->convertWorldPositionToTerrainSlot(cameraPos, &x, &y);
        if (tg->getTerrain(x, y) && tg->getTerrain(x, y)->isLoaded()) {
            float height = tg->getHeightAtWorldPosition(cameraPos);
            cameraPos.y = height + 10.0f;
        }
        ECS::get<Camera>().mCameraPivot->_setDerivedPosition(cameraPos);
        ECS::get<Camera>().mCameraPivot->_setDerivedOrientation(
            cursorOrientation);
        cameraPos =
            ECS::get<Camera>().mCameraGoal->_getDerivedPosition();
        ECS::get<Camera>().mCameraNode->_setDerivedPosition(cameraPos);
        ECS::get<Camera>().mCameraNode->_setDerivedOrientation(
            ECS::get<Camera>()
                .mCameraGoal->_getDerivedOrientation());
        // updateHeightmap();
    }
    void renderHierarchy(const Ogre::String &label, flecs::entity item,
                 ImGuiTreeNodeFlags flags,
                 const nlohmann::json::json_pointer &ptr,
                 nlohmann::json &j)
    {
        // Generate a unique ID for this node
        ImGui::PushID(
            static_cast<int>(reinterpret_cast<intptr_t>(&j[ptr])));

        // Set flags
        ImGuiTreeNodeFlags nodeFlags = flags;
        if (state.edited == item && state.edited_ptr == ptr)
            nodeFlags |= ImGuiTreeNodeFlags_Selected;

        // Render the node
        std::string displayLabel = label;
        if (!ptr.empty() && j[ptr].is_string() &&
            ptr.back() == "name") {
            // Optionally show value for name fields
            displayLabel += ": " + j[ptr].get<std::string>();
        }

        bool nodeOpen = ImGui::TreeNodeEx((void *)&j[ptr], nodeFlags,
                          "%s", displayLabel.c_str());

        // Handle click
        if (ImGui::IsItemClicked()) {
            state.edited = item;
            state.edited_ptr = ptr;
            std::string itemName = ptr.empty() ? "" : ptr.back();
            set_edit_type(itemName, ptr, j);
            std::cout << ptr << std::endl;
            {
                Ogre::String _jsonStr =
                    StaticGeometryModule::getItemProperties(
                        state.edited);
                nlohmann::json _j =
                    nlohmann::json::parse(_jsonStr);
                std::cout << _j[ptr].dump(4) << std::endl;
                if (_j[ptr].find("type") != _j[ptr].end()) {
                    _setCameraSelectedPos(state.edited);
                    /*
                    Ogre::Vector3 position;
                    Ogre::Quaternion orientation;
                    StaticGeometryModule::
                        getItemPositionAndRotation(
                            state.edited, position,
                            orientation);
                    ECS::get<Camera>()
                        .mCameraGoal
                        ->_setDerivedPosition(position);
                    ECS::get<Camera>()
                        .mCameraGoal
                        ->_setDerivedOrientation(
                            orientation);
                            */
                }
            }
        }

        // Render children
        if (nodeOpen) {
            std::string itemName = ptr.empty() ? "" : ptr.back();
            bool leaf = is_leaf(itemName, ptr);

            if (!leaf) {
                if (j[ptr].is_array()) {
                    for (size_t i = 0; i < j[ptr].size();
                         ++i) {
                        auto nptr = ptr / i;
                        renderHierarchy(
                            std::to_string(i), item,
                            flags, nptr, j);
                    }
                } else if (j[ptr].is_object()) {
                    nlohmann::json &obj = j[ptr];
                    for (auto it = obj.begin();
                         it != obj.end(); ++it) {
                        auto nptr = ptr / it.key();
                        renderHierarchy(it.key(), item,
                                flags, nptr, j);
                    }
                }
            }

            ImGui::TreePop();
        }

        ImGui::PopID();
    }
    // Property editor rendering using registry
    void renderPropertyEditor(flecs::entity entity,
                  const nlohmann::json::json_pointer &ptr,
                  int editType)
    {
        Ogre::String jsonStr =
            StaticGeometryModule::getItemProperties(entity);
        nlohmann::json j = nlohmann::json::parse(jsonStr);

        std::string itemName = ptr.empty() ? "" : ptr.back();
        PropertyEditor *editor = findEditor(editType, itemName, ptr, j);

        if (editor) {
            editor->render(entity, ptr, j);
        } else {
            // Fallback
            ImGui::TableSetColumnIndex(1);
            ImGui::TextWrapped("%s", j[ptr].dump(4).c_str());
        }
    }
    void drawMenu()
    {
        // Optional: Add a menu bar for "Add District/Lot" actions
        if (ImGui::BeginMenuBar()) {
            if (ImGui::BeginMenu("File")) { /* ... */
                ImGui::EndMenu();
            }
            ImGui::EndMenuBar();
        }
    }
    void drawHierarchy()
    {
        ImGui::BeginChild("HierarchyRegion", ImVec2(300, 400), true);
        std::list<std::pair<flecs::entity, Ogre::String> > items;
        StaticGeometryModule::getItemsProperties(&items);
        ImGuiTreeNodeFlags baseFlags =
            ImGuiTreeNodeFlags_OpenOnArrow |
            ImGuiTreeNodeFlags_SpanAvailWidth |
            ImGuiTreeNodeFlags_DefaultOpen;
        for (const auto &itemPair : items) {
            flecs::entity entity = itemPair.first;
            const Ogre::String &jsonStr = itemPair.second;

            ImGui::PushID(static_cast<int>(entity.id()));

            nlohmann::json j = nlohmann::json::parse(jsonStr);

            // Create label
            Ogre::String label =
                Ogre::StringConverter::toString(entity.id());
            label += ":" + j["type"].get<Ogre::String>();
            if (j.find("name") != j.end() &&
                !j["name"].get<Ogre::String>().empty())
                label = j["name"].get<Ogre::String>();

            // Render hierarchy for this item
            renderHierarchy(label, entity, baseFlags,
                    nlohmann::json::json_pointer(), j);

            ImGui::PopID();
        }
        ImGui::EndChild();
    }
    void drawPropertyEditor()
    {
        ImGui::BeginChild("PropertiesRegion", ImVec2(0, 0), true);
        ImGui::PushStyleVar(ImGuiStyleVar_CellPadding,
                    ImVec2(10.0f, 4.0f));
        if (ImGui::BeginTable("PropTable", 2,
                      ImGuiTableFlags_SizingStretchProp |
                          ImGuiTableFlags_Resizable)) {
            ImGui::TableSetupColumn(
                "Property", ImGuiTableColumnFlags_WidthFixed,
                0.0f);
            ImGui::TableSetupColumn(
                "Value", ImGuiTableColumnFlags_WidthStretch);
            ImGui::TableHeadersRow();
            ImGui::TableNextRow();
            if (state.edited.is_valid() &&
                !state.edited_ptr.empty()) {
                renderPropertyEditor(state.edited,
                             state.edited_ptr,
                             state.edited_type);
            } else if (state.edited.is_valid()) {
                // Show item summary when no specific property is selected
                ImGui::TableSetColumnIndex(0);
                ImGui::Text("Item");
                ImGui::TableSetColumnIndex(1);
                Ogre::String jsonStr =
                    StaticGeometryModule::getItemProperties(
                        state.edited);
                nlohmann::json j =
                    nlohmann::json::parse(jsonStr);
                ImGui::TextWrapped("%s", j.dump(4).c_str());
            }
            ImGui::EndTable();
        }
        if (ImGui::SmallButton("Run all town scripts")) {
            runAllScriptsForTown(state.edited);
            StaticGeometryModule::saveItems();
        }
        if (ImGui::SmallButton("Update world"))
            StaticGeometryModule::updateItemGeometry(state.edited);
        ImGui::SameLine();
        if (ImGui::SmallButton("Save items..."))
            StaticGeometryModule::saveItems();
        ImGui::PopStyleVar();
        // DrawPropertyInspector(); // Uses the Table logic from before
        ImGui::EndChild();
    }
    void drawCloseButton()
    {
        if (ImGui::Button("Close")) {
            ImGui::CloseCurrentPopup();
            state.clear();
        }
    }
};
static ItemEditor itemEditor;
void itemsEditorMenu()
{
    itemEditor.drawMenu();
}
void itemsEditorHierarchy()
{
    itemEditor.drawHierarchy();
}
void itemsEditorProperties()
{
    itemEditor.drawPropertyEditor();
}
void itemsEditor()
{
    if (ImGui::SmallButton("Edit items..."))
        ImGui::OpenPopup("Edit Items Popup");
    if (ImGui::BeginPopupModal("Edit Items Popup", NULL,
                   ImGuiWindowFlags_MenuBar)) {
        itemsEditorMenu();
        // --- LEFT SIDE: HIERARCHY ---
        itemsEditorHierarchy();
        ImGui::SameLine();

        // --- RIGHT SIDE: PROPERTIES ---
        itemsEditorProperties();
        itemEditor.drawCloseButton();

        ImGui::EndPopup();
    }
}
}
}
