#include "presentation/widgets/ConfigWidgets.h"

#include <algorithm>
#include <cstddef>
#include <string>
#include <utility>
#include <vector>

#include <imgui.h>

#include "presentation/action_feedback/ActionFeedbackPresenter.h"
#include "presentation/localization/UiLocalizationService.h"
#include "presentation/runtime/Viewport.h"
#include "presentation/theme/MenuTheme.h"
#include "presentation/widgets/BasicWidgets.h"

namespace ta::presentation::widgets
{
    namespace
    {
        std::string widget_label(const ConfigValueSnapshot& value)
        {
            return ta::presentation::localization::tr(value.label_key) + "##" + value.key;
        }

        void submit_set_value(const ConfigValueSnapshot& value,
                              const ConfigCommandSubmitter& submit,
                              ta::application::config::ConfigValueData data)
        {
            if (!submit || !value.valid())
                return;

            submit({ConfigCommandType::SetValue, value.key, std::move(data)});
        }

        void submit_action(const ConfigValueSnapshot& value, const ConfigCommandSubmitter& submit)
        {
            if (!submit || !value.valid())
                return;

            ConfigCommand command{ConfigCommandType::InvokeAction, value.key, {}};
            command.trace_id = ActionFeedbackPresenter::begin(
                value.key,
                ta::presentation::localization::tr(value.label_key),
                "Actions");
            submit(std::move(command));
        }

        void draw_row_label(const ConfigValueSnapshot& value)
        {
            ImGui::TextUnformatted(ta::presentation::localization::tr(value.label_key).c_str());
        }

        bool draw_esp_toggle_color_row(const ConfigValueSnapshot& toggle,
                                       const ConfigValueSnapshot& color,
                                       const ConfigCommandSubmitter& submit)
        {
            const float sf = ta::presentation::runtime::scale_factor();
            bool changed = false;
            bool enabled = toggle.bool_value;
            float rgba[4] = {
                color.color_value.r,
                color.color_value.g,
                color.color_value.b,
                color.color_value.a
            };

            ImGui::PushID(toggle.key.c_str());
            if (ImGui::BeginTable("##esp_toggle_color_row", 3,
                                  ImGuiTableFlags_SizingStretchProp |
                                  ImGuiTableFlags_NoSavedSettings))
            {
                ImGui::TableSetupColumn("##label", ImGuiTableColumnFlags_WidthStretch, 0.58f);
                ImGui::TableSetupColumn("##toggle", ImGuiTableColumnFlags_WidthFixed, 96.0f * sf);
                ImGui::TableSetupColumn("##color", ImGuiTableColumnFlags_WidthFixed, 94.0f * sf);
                ImGui::TableNextRow();

                ImGui::TableSetColumnIndex(0);
                ImGui::AlignTextToFramePadding();
                draw_row_label(toggle);

                ImGui::TableSetColumnIndex(1);
                if (toggle_button("toggle", &enabled, 90.0f))
                {
                    submit_set_value(toggle, submit, enabled);
                    changed = true;
                }

                ImGui::TableSetColumnIndex(2);
                ImGui::BeginDisabled(!enabled);
                if (compact_colorpicker(widget_label(color), rgba))
                {
                    submit_set_value(color, submit, ta::application::config::ConfigColorData{rgba[0], rgba[1], rgba[2], rgba[3]});
                    changed = true;
                }
                ImGui::EndDisabled();
                ImGui::EndTable();
            }

            ImGui::PopID();
            return changed;
        }

        bool should_pair_esp_color(const std::vector<ConfigValueSnapshot>& values, const std::size_t index)
        {
            return index + 1 < values.size() &&
                   values[index].kind == ConfigControlKind::Checkbox &&
                   values[index + 1].kind == ConfigControlKind::Colorpicker;
        }
    }

    void draw_config_value(const ConfigValueSnapshot& value, const ConfigCommandSubmitter& submit)
    {
        if (!value.valid())
            return;

        switch (value.kind)
        {
        case ConfigControlKind::ActionRow:
        {
            const bool pending = ActionFeedbackPresenter::is_pending(value.key);
            ImGui::BeginDisabled(pending);
            const std::string label = pending
                ? ta::presentation::localization::tr("Queued") + "...##" + value.key
                : ta::presentation::localization::tr(value.label_key) + "##" + value.key;
            if (action_button(label, 180.0f))
                submit_action(value, submit);
            ImGui::EndDisabled();

            const std::string feedback = ActionFeedbackPresenter::status_text(value.key);
            if (!feedback.empty())
                ImGui::TextColored(ta::presentation::theme::palette::muted, "%s", feedback.c_str());
            break;
        }
        case ConfigControlKind::Checkbox:
        {
            bool current = value.bool_value;
            if (checkbox(widget_label(value), &current))
                submit_set_value(value, submit, current);
            break;
        }
        case ConfigControlKind::Colorpicker:
        {
            float rgba[4] = {
                value.color_value.r,
                value.color_value.g,
                value.color_value.b,
                value.color_value.a
            };
            if (colorpicker(widget_label(value), rgba))
                submit_set_value(value, submit, ta::application::config::ConfigColorData{rgba[0], rgba[1], rgba[2], rgba[3]});
            break;
        }
        case ConfigControlKind::FloatSlider:
        {
            float current = value.float_value;
            if (slider_float(widget_label(value), &current, value.float_min, value.float_max, value.float_step))
                submit_set_value(value, submit, current);
            break;
        }
        case ConfigControlKind::IntSlider:
        {
            int current = value.int_value;
            if (slider_int(widget_label(value), &current, value.int_min, value.int_max))
                submit_set_value(value, submit, current);
            break;
        }
        case ConfigControlKind::IntTextbox:
        {
            int current = value.int_value;
            if (int_textbox(widget_label(value), &current))
                submit_set_value(value, submit, current);
            break;
        }
        case ConfigControlKind::Keybind:
        {
            int current = value.int_value;
            if (ta::application::IKeyInput* input = submit.key_input();
                input && keybind(widget_label(value), &current, value.allow_none, *input))
            {
                submit_set_value(value, submit, current);
            }
            break;
        }
        case ConfigControlKind::Select:
        {
            int current = value.int_value;
            if (select(widget_label(value), &current, value.options))
                submit_set_value(value, submit, current);
            break;
        }
        case ConfigControlKind::Textbox:
        {
            std::string current = value.text_value;
            if (textbox(widget_label(value), &current))
                submit_set_value(value, submit, std::move(current));
            break;
        }
        default:
            break;
        }
    }

    void draw_config_panel(const ConfigPanelSnapshot& panel, const ConfigCommandSubmitter& submit)
    {
        const float sf = ta::presentation::runtime::scale_factor();
        ImGui::PushID(panel.id.c_str());

        const std::string panel_name = ta::presentation::localization::tr(panel.label_key);
        if (ImGui::CollapsingHeader(panel_name.c_str(), ImGuiTreeNodeFlags_DefaultOpen))
        {
            ImGui::Indent(6.0f * sf);
            const bool esp_panel = panel.id == "esp";
            for (std::size_t i = 0; i < panel.values.size(); ++i)
            {
                if (esp_panel && should_pair_esp_color(panel.values, i))
                {
                    draw_esp_toggle_color_row(panel.values[i], panel.values[i + 1], submit);
                    ++i;
                    continue;
                }

                draw_config_value(panel.values[i], submit);
            }
            ImGui::Unindent(6.0f * sf);
        }

        ImGui::PopID();
    }

    void draw_config_panels_single_column(const std::vector<const ConfigPanelSnapshot*>& panels,
                                          const ConfigCommandSubmitter& submit)
    {
        ImGui::BeginChild("##config_owner_single", ImGui::GetContentRegionAvail(), false);
        for (const ConfigPanelSnapshot* panel : panels)
        {
            if (panel)
                draw_config_panel(*panel, submit);
        }
        ImGui::EndChild();
    }

    void draw_config_value_disabled(const ConfigValueSnapshot& value,
                                    const ConfigCommandSubmitter& submit,
                                    const bool disabled)
    {
        ImGui::BeginDisabled(disabled);
        draw_config_value(value, submit);
        ImGui::EndDisabled();
    }

    bool draw_toggle_color_distance_row(const ConfigValueSnapshot& toggle,
                                        const ConfigValueSnapshot& color,
                                        const ConfigValueSnapshot* distance,
                                        const ConfigCommandSubmitter& submit)
    {
        if (!toggle.valid() || !color.valid())
            return false;

        const float sf = ta::presentation::runtime::scale_factor();
        const bool has_distance = distance && distance->valid();
        bool changed = false;
        bool enabled = toggle.bool_value;
        float rgba[4] = {color.color_value.r, color.color_value.g, color.color_value.b, color.color_value.a};

        ImGui::PushID(toggle.key.c_str());
        const int columns = has_distance ? 4 : 3;
        if (ImGui::BeginTable("##toggle_color_distance", columns,
                              ImGuiTableFlags_SizingStretchProp |
                              ImGuiTableFlags_NoSavedSettings))
        {
            ImGui::TableSetupColumn("##label", ImGuiTableColumnFlags_WidthStretch, 0.48f);
            ImGui::TableSetupColumn("##toggle", ImGuiTableColumnFlags_WidthFixed, 96.0f * sf);
            ImGui::TableSetupColumn("##color", ImGuiTableColumnFlags_WidthFixed, 90.0f * sf);
            if (has_distance)
                ImGui::TableSetupColumn("##distance", ImGuiTableColumnFlags_WidthFixed, 118.0f * sf);
            ImGui::TableNextRow();

            ImGui::TableSetColumnIndex(0);
            ImGui::AlignTextToFramePadding();
            draw_row_label(toggle);

            ImGui::TableSetColumnIndex(1);
            if (toggle_button("toggle", &enabled, 90.0f))
            {
                submit_set_value(toggle, submit, enabled);
                changed = true;
            }

            ImGui::TableSetColumnIndex(2);
            ImGui::BeginDisabled(!enabled);
            if (compact_colorpicker(widget_label(color), rgba))
            {
                submit_set_value(color, submit, ta::application::config::ConfigColorData{rgba[0], rgba[1], rgba[2], rgba[3]});
                changed = true;
            }
            ImGui::EndDisabled();

            if (has_distance)
            {
                ImGui::TableSetColumnIndex(3);
                ImGui::BeginDisabled(!enabled);
                float current = distance->float_value;
                ImGui::SetNextItemWidth(-1.0f);
                if (ImGui::DragFloat("##distance", &current, distance->float_step,
                                     distance->float_min, distance->float_max, "%.0f m"))
                {
                    current = (std::clamp)(current, distance->float_min, distance->float_max);
                    submit_set_value(*distance, submit, current);
                    changed = true;
                }
                ImGui::EndDisabled();
            }

            ImGui::EndTable();
        }

        ImGui::PopID();
        return changed;
    }

    void draw_config_panels_two_columns(const std::vector<const ConfigPanelSnapshot*>& panels,
                                        const ConfigCommandSubmitter& submit)
    {
        const TwoColumnLayout layout = two_column_layout();
        if (layout.stacked)
        {
            draw_config_panels_single_column(panels, submit);
            return;
        }

        if (ImGui::BeginTable("##config_owner_columns", 2,
                              ImGuiTableFlags_SizingStretchSame |
                              ImGuiTableFlags_BordersInnerV |
                              ImGuiTableFlags_NoSavedSettings))
        {
            ImGui::TableNextRow();
            ImGui::TableSetColumnIndex(0);
            ImGui::BeginChild("##config_owner_left", ImVec2(0.0f, layout.height), false);
            for (std::size_t i = 0; i < panels.size(); i += 2)
            {
                if (panels[i])
                    draw_config_panel(*panels[i], submit);
            }
            ImGui::EndChild();

            ImGui::TableSetColumnIndex(1);
            ImGui::BeginChild("##config_owner_right", ImVec2(0.0f, layout.height), false);
            for (std::size_t i = 1; i < panels.size(); i += 2)
            {
                if (panels[i])
                    draw_config_panel(*panels[i], submit);
            }
            ImGui::EndChild();
            ImGui::EndTable();
        }
    }
}
