#include "presentation/MenuRenderer.h"
#include "application/lifecycle/RaidContext.h"

#include <algorithm>
#include <array>
#include <string>

#include "foundation/product/ProductInfo.h"
#include "foundation/diagnostics/CrashDiagnostics.h"
#include "presentation/localization/UiLocalizationService.h"
#include "foundation/input/KeyNames.h"
#include "application/messaging/PresentationState.h"
#include "presentation/MenuController.h"
#include "presentation/overlays/esp/EspOverlayRenderer.h"
#include "presentation/overlays/watermark/WatermarkOverlayRenderer.h"
#include "presentation/pages/ConfigPage.h"
#include "presentation/pages/DiagnosticsPage.h"
#include "presentation/pages/items/ItemsPage.h"
#include "presentation/pages/quests/QuestsPage.h"
#include "presentation/theme/MenuTheme.h"
#include "presentation/runtime/Viewport.h"
#include "presentation/fonts/MenuFontAtlas.h"
#include "presentation/widgets/BasicWidgets.h"
#include "presentation/action_feedback/ActionFeedbackPresenter.h"

namespace
{
    struct PageNavItem
    {
        ta::presentation::pages::PageId id;
        const char* title;
        const char* description;
    };

    constexpr std::array<PageNavItem, 6> page_nav_items = {{
        {ta::presentation::pages::PageId::Visuals, "VISUALS", "PMC, loot and container overlays"},
        {ta::presentation::pages::PageId::Raid, "RAID", "player, movement, progression and world controls"},
        {ta::presentation::pages::PageId::Quests, "QUESTS", "quest actions and task management"},
        {ta::presentation::pages::PageId::Items, "ITEMS", "generate items and search the catalog"},
        {ta::presentation::pages::PageId::Settings, "SETTINGS", "application configuration"},
        {ta::presentation::pages::PageId::Diagnostics, "DIAGNOSTICS", "performance, runtime status and execution logs"}
    }};

    bool is_page_available(const ta::presentation::MenuContext&, const ta::presentation::pages::PageId)
    {
        return true;
    }

    const PageNavItem* find_nav_item(const ta::presentation::MenuContext& ctx, const ta::presentation::pages::PageId id)
    {
        for (const PageNavItem& item : page_nav_items)
        {
            if (item.id == id && is_page_available(ctx, item.id))
                return &item;
        }

        return nullptr;
    }

    const PageNavItem* first_available_page(const ta::presentation::MenuContext& ctx)
    {
        for (const PageNavItem& item : page_nav_items)
        {
            if (is_page_available(ctx, item.id))
                return &item;
        }

        return nullptr;
    }

    void draw_page_title(const char* title, const char* description)
    {
        const std::string translated_title = ta::presentation::localization::tr(title);
        const std::string translated_description = ta::presentation::localization::tr(description);
        const bool stacked = ta::presentation::theme::compact_for_width(
            ImGui::GetContentRegionAvail().x,
            200.0f);

        ImGui::PushFont(ta::presentation::fonts::bold_font());
        ImGui::TextColored(ta::presentation::theme::palette::accent, "%s", translated_title.c_str());
        ImGui::PopFont();

        if (!stacked)
            ImGui::SameLine();
        ImGui::PushTextWrapPos(0.0f);
        ImGui::TextColored(ta::presentation::theme::palette::muted, "%s", translated_description.c_str());
        ImGui::PopTextWrapPos();

        ImGui::Separator();
        ImGui::Spacing();
    }

    void update_menu_toggle(ta::presentation::UiState& state, const ta::presentation::MenuContext& ctx)
    {
        if (ta::presentation::widgets::is_keybind_capturing())
            return;

        int menu_key = 0x2D;
        if (ta::foundation::input::is_valid_key_index(ctx.menu_key))
            menu_key = ctx.menu_key;

        if (!ctx.key_input)
            return;

        if (state.open && ctx.key_input->pressed(0x1B))
        {
            state.open = false;
            return;
        }

        if (ctx.key_input->pressed(menu_key))
            state.open = !state.open;
    }

    void draw_current_page(const ta::presentation::MenuContext& ctx, ta::presentation::UiState& state)
    {
        switch (state.selected_page)
        {
        case ta::presentation::pages::PageId::Visuals:
            ta::foundation::diagnostics::crash::set_stage("ui.page.visuals");
            ta::presentation::pages::draw_config_category_page(ctx, ta::presentation::pages::PageId::Visuals);
            break;
        case ta::presentation::pages::PageId::Raid:
            ta::foundation::diagnostics::crash::set_stage("ui.page.raid");
            ta::presentation::pages::draw_raid_config_page(ctx, state.progression);
            break;
        case ta::presentation::pages::PageId::Quests:
            ta::foundation::diagnostics::crash::set_stage("ui.page.quests");
            ta::presentation::pages::quests::draw_quests_page(ctx, state);
            break;
        case ta::presentation::pages::PageId::Items:
            ta::foundation::diagnostics::crash::set_stage("ui.page.items");
            ta::presentation::pages::items::draw_items_page(ctx, state);
            break;
        case ta::presentation::pages::PageId::Diagnostics:
            ta::foundation::diagnostics::crash::set_stage("ui.page.diagnostics");
            ta::presentation::pages::draw_diagnostics_page(ctx, state);
            break;
        case ta::presentation::pages::PageId::Settings:
        default:
            ta::foundation::diagnostics::crash::set_stage("ui.page.settings");
            ta::presentation::pages::draw_config_category_page(ctx, ta::presentation::pages::PageId::Settings, true, true);
            break;
        }
    }
}

void ta::presentation::MenuRenderer::draw(ta::presentation::UiState& state, const ta::presentation::MenuContext& ctx)
{
    if (!ctx.valid)
        return;

    if (!find_nav_item(ctx, state.selected_page))
    {
        if (const PageNavItem* first = first_available_page(ctx))
            state.selected_page = first->id;
    }

    ta::foundation::diagnostics::crash::set_stage("ui.renderer.menu-toggle");
    update_menu_toggle(state, ctx);
    ta::foundation::diagnostics::crash::set_stage("ui.renderer.feedback-update");
    ta::presentation::ActionFeedbackPresenter::update();

    ta::foundation::diagnostics::crash::set_stage("ui.renderer.theme");

    ImGuiIO& io = ImGui::GetIO();

    // EFT may keep the native cursor hidden or locked. Draw an ImGui cursor
    // while the menu is open so the overlay remains usable independently of
    // the game's cursor state.
    io.MouseDrawCursor = state.open;
    ImGui::SetMouseCursor(ImGuiMouseCursor_Arrow);

    const float dt = io.DeltaTime;
    state.animation += ((state.open ? 1.0f : 0.0f) - state.animation) * (std::min)(1.0f, dt * 14.0f);

    const float sf = ta::presentation::runtime::scale_factor();

    if (state.animation > 0.004f)
    {
        const ta::presentation::theme::UiMetrics metrics = ta::presentation::theme::ui_metrics();
        const ImGuiViewport* viewport = ImGui::GetMainViewport();
        const ImVec2 work_size = viewport ? viewport->WorkSize : io.DisplaySize;
        const ImVec2 work_origin = viewport ? viewport->WorkPos : ImVec2(0.0f, 0.0f);
        const ImVec2 maximum_size{
            (std::max)(320.0f, work_size.x * 0.96f),
            (std::max)(260.0f, work_size.y * 0.96f)};
        const ImVec2 minimum_size{
            (std::min)(metrics.window_min_width, maximum_size.x),
            (std::min)(metrics.window_min_height, maximum_size.y)};
        const ImVec2 desired_size{
            (std::clamp)(metrics.window_width, minimum_size.x, maximum_size.x),
            (std::clamp)(metrics.window_height, minimum_size.y, maximum_size.y)};

        ImGui::SetNextWindowSizeConstraints(minimum_size, maximum_size);
        ImGui::SetNextWindowSize(desired_size, ImGuiCond_Appearing);
        ImGui::SetNextWindowPos(
            ImVec2(work_origin.x + work_size.x * 0.5f, work_origin.y + work_size.y * 0.5f),
            ImGuiCond_Appearing,
            ImVec2(0.5f, 0.5f));
        ImGui::SetNextWindowBgAlpha(state.animation);

        ImGui::PushStyleVar(ImGuiStyleVar_Alpha, state.animation);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));

        if (ImGui::Begin(
            ta::foundation::product_info::display_title().c_str(),
            &state.open,
            ImGuiWindowFlags_NoCollapse |
            ImGuiWindowFlags_NoScrollbar
        ))
        {
            const float window_width = ImGui::GetContentRegionAvail().x;
            const float sidebar_width = (std::clamp)(
                window_width * 0.16f,
                metrics.sidebar_min_width,
                metrics.sidebar_max_width);

            const ImVec2 origin = ImGui::GetCursorScreenPos();
            const ImVec2 available = ImGui::GetContentRegionAvail();

            ImDrawList* draw_list = ImGui::GetWindowDrawList();

            draw_list->AddRectFilled(
                origin,
                ImVec2(origin.x + sidebar_width, origin.y + available.y),
                ImGui::GetColorU32(ta::presentation::theme::with_alpha(ta::presentation::theme::palette::surface, 0.72f))
            );

            draw_list->AddLine(
                ImVec2(origin.x + sidebar_width, origin.y + 8.0f * sf),
                ImVec2(origin.x + sidebar_width, origin.y + available.y - 8.0f * sf),
                ImGui::GetColorU32(ta::presentation::theme::palette::line)
            );

            const float nav_top = 16.0f * sf;
            const float nav_height = 42.0f * sf;
            const float nav_left = 8.0f * sf;
            constexpr std::size_t nav_columns = 1;
            const float nav_width = sidebar_width - nav_left * 2.0f;
            size_t visible_index = 0;

            for (const PageNavItem& item : page_nav_items)
            {
                if (!is_page_available(ctx, item.id))
                    continue;

                const std::size_t row = visible_index / nav_columns;
                const std::size_t column = visible_index % nav_columns;
                ImGui::SetCursorPos(ImVec2(
                    nav_left + static_cast<float>(column) * nav_width,
                    nav_top + static_cast<float>(row) * nav_height));

                ImGui::PushID(static_cast<int>(visible_index));

                if (ImGui::InvisibleButton(
                    "sidebar_nav",
                    ImVec2(nav_width, nav_height - 6.0f * sf)
                ))
                {
                    state.selected_page = item.id;
                }

                const bool hovered = ImGui::IsItemHovered();
                const bool focused = ImGui::IsItemFocused();
                if (hovered)
                    ImGui::SetTooltip("%s", ta::presentation::localization::tr(item.description).c_str());

                ImGui::PopID();

                const ImVec2 min = ImGui::GetItemRectMin();
                const ImVec2 max = ImGui::GetItemRectMax();
                const bool selected = state.selected_page == item.id;

                if (selected || hovered || focused)
                {
                    draw_list->AddRectFilled(
                        min,
                        max,
                        ImGui::GetColorU32(ta::presentation::theme::with_alpha(
                            selected ? ta::presentation::theme::palette::accent : ta::presentation::theme::palette::accent2,
                            selected ? 0.18f : (focused ? 0.14f : 0.08f)
                        )),
                        6.0f * sf
                    );
                }

                if (selected)
                {
                    draw_list->AddRectFilled(
                        ImVec2(min.x, min.y + 4.0f * sf),
                        ImVec2(min.x + 3.0f * sf, max.y - 4.0f * sf),
                        ImGui::GetColorU32(ta::presentation::theme::palette::accent),
                        2.0f * sf
                    );
                }

                const std::string navigation_label = ta::presentation::localization::tr(item.title);

                draw_list->AddText(
                    ImVec2(min.x + 10.0f * sf, min.y + 9.0f * sf),
                    ImGui::GetColorU32(selected ? ta::presentation::theme::palette::text : ta::presentation::theme::palette::muted),
                    navigation_label.c_str()
                );

                ++visible_index;
            }

            const float content_x = sidebar_width + 20.0f * sf;
            const float content_y = 18.0f * sf;
            const float content_width = (std::max)(1.0f, available.x - sidebar_width - 38.0f * sf);
            const float content_height = (std::max)(1.0f, available.y - 34.0f * sf);

            ImGui::SetCursorPos(ImVec2(content_x, content_y));

            ImGui::BeginChild(
                "##sidebar_content",
                ImVec2(content_width, content_height),
                false
            );

            if (const PageNavItem* current = find_nav_item(ctx, state.selected_page))
            {
                ta::foundation::diagnostics::crash::set_stage("ui.renderer.page-title");
                draw_page_title(current->title, current->description);
                draw_current_page(ctx, state);
            }

            ImGui::EndChild();
        }

        ImGui::End();

        ImGui::PopStyleVar(2);
    }

    ta::foundation::diagnostics::crash::set_stage("ui.renderer.publish-menu-state");
    ta::application::presentation_state::menu_visible.publish(state.open);

    const bool item_sidebar_active = state.open && state.selected_page == ta::presentation::pages::PageId::Items;
    if (state.item_sidebar_active != item_sidebar_active)
    {
        state.item_sidebar_active = item_sidebar_active;
        if (ctx.items.commands)
            ctx.items.commands->set_selector_active(item_sidebar_active);
    }

    ImDrawList* overlay = ImGui::GetBackgroundDrawList();

    if (overlay)
    {
        ta::foundation::diagnostics::crash::set_stage("ui.overlay.watermark");
        ta::presentation::overlays::watermark::WatermarkOverlayRenderer::draw(overlay, ctx.watermark);
        if (!ta::application::raid_context::teardown_in_progress())
        {
            ta::foundation::diagnostics::crash::set_stage("ui.overlay.esp");
            ta::presentation::overlays::esp::EspOverlayRenderer::draw(overlay, ctx.esp);
        }
        else
        {
            ta::foundation::diagnostics::crash::set_stage("ui.overlay.esp.skipped-teardown");
        }
    }

    ta::foundation::diagnostics::crash::set_stage("ui.overlay.action-feedback");
    ta::presentation::ActionFeedbackPresenter::draw_toasts();
    ta::foundation::diagnostics::crash::set_stage("ui.renderer.complete");
}
