#include "core/toast.h"
#include "core/tokens.h"
#include "core/icons_tabler.h"
#include <imgui.h>
#include <chrono>
#include <deque>
#include <mutex>
#include <string>
#include <vector>

namespace fn_ui {

namespace {

struct Toast {
    ToastKind kind;
    std::string text;
    std::chrono::steady_clock::time_point created;
};

constexpr float kToastDurationSec = 3.5f;
constexpr float kFadeOutSec       = 0.6f;
constexpr float kToastWidth       = 320.0f;
constexpr float kToastVerticalGap = 8.0f;
constexpr float kToastMarginX     = 16.0f;
constexpr float kToastMarginY     = 16.0f;
constexpr int   kHistoryMax       = 50;

// Cola de toasts activos + historial completo (hasta kHistoryMax).
std::deque<Toast> g_queue;
std::deque<Toast> g_history;    // mas reciente al final
int               g_unread = 0; // no-leidos desde la ultima vez que se abrio el inbox
std::mutex        g_mutex;

ImVec4 color_for(ToastKind k) {
    using namespace fn_tokens::colors;
    switch (k) {
        case ToastKind::Info:    return info;
        case ToastKind::Success: return success;
        case ToastKind::Warning: return warning;
        case ToastKind::Error:   return error;
    }
    return info;
}

const char* glyph_for(ToastKind k) {
    switch (k) {
        case ToastKind::Info:    return "\xe2\x84\xb9"; // i
        case ToastKind::Success: return "\xe2\x9c\x93"; // check
        case ToastKind::Warning: return "\xe2\x9a\xa0"; // warning
        case ToastKind::Error:   return "\xe2\x9c\x97"; // x
    }
    return "";
}

std::string format_age(const std::chrono::steady_clock::time_point& t,
                       const std::chrono::steady_clock::time_point& now) {
    auto s = std::chrono::duration_cast<std::chrono::seconds>(now - t).count();
    char buf[32];
    if (s < 60)         std::snprintf(buf, sizeof(buf), "%llds", (long long)s);
    else if (s < 3600)  std::snprintf(buf, sizeof(buf), "%lldm", (long long)(s / 60));
    else                std::snprintf(buf, sizeof(buf), "%lldh", (long long)(s / 3600));
    return buf;
}

} // namespace

void toast_push(ToastKind kind, const char* text) {
    const char* safe = (text && *text) ? text : "(no message)";
    std::lock_guard<std::mutex> lk(g_mutex);
    auto now = std::chrono::steady_clock::now();
    Toast t{kind, std::string(safe), now};
    g_queue.push_back(t);
    g_history.push_back(t);
    while ((int)g_history.size() > kHistoryMax) g_history.pop_front();
    g_unread++;
}

void toast_render() {
    using namespace fn_tokens;
    std::lock_guard<std::mutex> lk(g_mutex);
    if (g_queue.empty()) return;

    const auto now = std::chrono::steady_clock::now();
    while (!g_queue.empty()) {
        float age = std::chrono::duration<float>(now - g_queue.front().created).count();
        if (age > kToastDurationSec) g_queue.pop_front();
        else break;
    }
    if (g_queue.empty()) return;

    const ImGuiViewport* vp = ImGui::GetMainViewport();
    float cur_y = vp->WorkPos.y + vp->WorkSize.y - kToastMarginY;
    const float x = vp->WorkPos.x + vp->WorkSize.x - kToastMarginX - kToastWidth;

    int i = 0;
    for (const auto& t : g_queue) {
        float age = std::chrono::duration<float>(now - t.created).count();
        float alpha = 1.0f;
        if (age > kToastDurationSec - kFadeOutSec) {
            alpha = (kToastDurationSec - age) / kFadeOutSec;
            if (alpha < 0.0f) alpha = 0.0f;
        }

        ImVec4 accent = color_for(t.kind);
        ImVec4 surf   = colors::surface;
        surf.w       *= alpha;
        accent.w     *= alpha;

        ImGui::SetNextWindowPos(ImVec2(x, cur_y), ImGuiCond_Always, ImVec2(0, 1));
        ImGui::SetNextWindowSize(ImVec2(kToastWidth, 0));
        ImGui::PushStyleColor(ImGuiCol_WindowBg, surf);
        ImGui::PushStyleColor(ImGuiCol_Border,   accent);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding,   radius::md);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
        ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,    ImVec2(spacing::md, spacing::sm));
        ImGui::PushStyleVar(ImGuiStyleVar_Alpha,            alpha);

        char id[32];
        std::snprintf(id, sizeof(id), "##toast_%d", i);
        ImGui::Begin(id, nullptr,
            ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize
          | ImGuiWindowFlags_NoMove     | ImGuiWindowFlags_NoScrollbar
          | ImGuiWindowFlags_NoInputs   | ImGuiWindowFlags_NoSavedSettings
          | ImGuiWindowFlags_AlwaysAutoResize);

        ImGui::PushStyleColor(ImGuiCol_Text, accent);
        ImGui::TextUnformatted(glyph_for(t.kind));
        ImGui::PopStyleColor();
        ImGui::SameLine();
        ImGui::TextWrapped("%s", t.text.c_str());

        ImVec2 sz = ImGui::GetWindowSize();
        ImGui::End();

        ImGui::PopStyleVar(4);
        ImGui::PopStyleColor(2);

        cur_y -= (sz.y + kToastVerticalGap);
        i++;
    }
}

int toast_unread_count() {
    std::lock_guard<std::mutex> lk(g_mutex);
    return g_unread;
}

void toast_history_clear() {
    std::lock_guard<std::mutex> lk(g_mutex);
    g_history.clear();
    g_unread = 0;
}

void toast_inbox_button(const char* id) {
    using namespace fn_tokens;

    // Snapshot tread-safe del estado
    int unread = 0;
    std::vector<Toast> snapshot;
    {
        std::lock_guard<std::mutex> lk(g_mutex);
        unread = g_unread;
        snapshot.assign(g_history.begin(), g_history.end());
    }

    // Estilo del boton: subtle como icon_button
    ImGui::PushStyleColor(ImGuiCol_Button,        ImVec4(0, 0, 0, 0));
    ImGui::PushStyleColor(ImGuiCol_ButtonHovered, colors::surface_hover);
    ImGui::PushStyleColor(ImGuiCol_ButtonActive,  colors::surface);
    ImGui::PushStyleColor(ImGuiCol_Text,          colors::text);
    ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, radius::sm);
    ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(spacing::xs, spacing::xs));

    // Label: TI_BELL (Tabler icon, dentro del rango 0xE000-0xFCFF cargado por
    // icon_font.cpp). Antes usabamos un emoji U+1F514 (4-byte UTF-8) que cae
    // fuera del rango y se renderizaba como '?'.
    char label[64];
    std::snprintf(label, sizeof(label), TI_BELL "%s", id);

    ImVec2 btn_pos = ImGui::GetCursorScreenPos();
    const bool clicked = ImGui::Button(label, ImVec2(28.0f, 28.0f));
    ImVec2 btn_max = ImGui::GetItemRectMax();

    ImGui::PopStyleVar(2);
    ImGui::PopStyleColor(4);

    // Badge: circulo rojo con numero en la esquina superior derecha
    if (unread > 0) {
        ImDrawList* dl = ImGui::GetWindowDrawList();
        const float r = 7.0f;
        ImVec2 c(btn_max.x - r + 2.0f, btn_pos.y + r - 2.0f);
        dl->AddCircleFilled(c, r, ImGui::ColorConvertFloat4ToU32(colors::error));
        char txt[8];
        if (unread > 9) std::snprintf(txt, sizeof(txt), "9+");
        else            std::snprintf(txt, sizeof(txt), "%d", unread);
        ImVec2 ts = ImGui::CalcTextSize(txt);
        dl->AddText(ImVec2(c.x - ts.x * 0.5f, c.y - ts.y * 0.5f),
                    IM_COL32_WHITE, txt);
    }

    char popup_id[64];
    std::snprintf(popup_id, sizeof(popup_id), "##inbox_popup%s", id);

    // Posicion fija del popup = anclada al boton en el frame del click.
    // Acotada al WorkRect del viewport PRIMARIO (IMPORTANTE: con viewports
    // activados, una posicion fuera del WorkRect se renderiza en otra
    // ventana del OS / otro monitor).
    static ImVec2 s_popup_anchor{0, 0};
    if (clicked) {
        const ImGuiViewport* vp = ImGui::GetMainViewport();
        constexpr float popup_w = 360.0f;
        constexpr float popup_h = 360.0f;
        float x = btn_max.x - popup_w;                    // alinear lado derecho con el boton
        float y = btn_max.y + 4.0f;
        // Clamp dentro del WorkRect del viewport principal
        if (x < vp->WorkPos.x) x = vp->WorkPos.x;
        if (y < vp->WorkPos.y) y = vp->WorkPos.y;
        if (x + popup_w > vp->WorkPos.x + vp->WorkSize.x)
            x = vp->WorkPos.x + vp->WorkSize.x - popup_w;
        if (y + popup_h > vp->WorkPos.y + vp->WorkSize.y)
            y = vp->WorkPos.y + vp->WorkSize.y - popup_h;
        s_popup_anchor = ImVec2(x, y);
        ImGui::SetNextWindowViewport(vp->ID); // pinear al viewport principal
        ImGui::OpenPopup(popup_id);
        std::lock_guard<std::mutex> lk(g_mutex);
        g_unread = 0;
    }
    ImGui::SetNextWindowPos(s_popup_anchor, ImGuiCond_Appearing);

    // Popover del inbox
    ImGui::PushStyleColor(ImGuiCol_PopupBg, colors::surface);
    ImGui::PushStyleColor(ImGuiCol_Border,  colors::border);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding,   radius::md);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding,    ImVec2(spacing::sm, spacing::sm));

    // Tamano FIJO del popup (Always + SizeConstraints) para evitar feedback
    // loop entre auto-resize del popup y TextWrapped/SameLine internos.
    constexpr float kInboxW = 360.0f;
    constexpr float kInboxH = 380.0f;
    ImGui::SetNextWindowSize(ImVec2(kInboxW, kInboxH), ImGuiCond_Always);
    ImGui::SetNextWindowSizeConstraints(ImVec2(kInboxW, kInboxH),
                                        ImVec2(kInboxW, kInboxH));
    if (ImGui::BeginPopup(popup_id, ImGuiWindowFlags_NoResize
                                  | ImGuiWindowFlags_NoMove)) {
        // Header — usar offset desde la izquierda calculado a partir del ancho
        // FIJO del popup, NO de GetWindowContentRegionMax() (que cambia frame
        // a frame con padding y provoca oscilacion horizontal).
        const float content_w = kInboxW - 2.0f * spacing::sm;
        ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted);
        ImGui::TextUnformatted("Notifications");
        ImGui::PopStyleColor();
        ImGui::SameLine(content_w - 50.0f);
        if (ImGui::SmallButton("Clear")) {
            toast_history_clear();
            snapshot.clear();
        }
        ImGui::Separator();

        if (snapshot.empty()) {
            ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim);
            ImGui::TextUnformatted("(no notifications yet)");
            ImGui::PopStyleColor();
        } else {
            const auto now = std::chrono::steady_clock::now();
            // Reservar el espacio restante para la lista — alto fijo evita
            // oscilacion vertical del popup.
            ImGui::BeginChild("##inbox_list", ImVec2(content_w, 0.0f));
            const float list_w = ImGui::GetContentRegionAvail().x;
            // Mostrar mas reciente arriba
            for (auto it = snapshot.rbegin(); it != snapshot.rend(); ++it) {
                const auto& t = *it;
                ImGui::PushStyleColor(ImGuiCol_Text, color_for(t.kind));
                ImGui::TextUnformatted(glyph_for(t.kind));
                ImGui::PopStyleColor();
                ImGui::SameLine();
                // Wrap manual hasta x = list_w - 30 (espacio para el age),
                // usando coordenadas del cursor relativas al window.
                const float wrap_x = ImGui::GetCursorPosX() + (list_w - ImGui::GetCursorPosX()) - 36.0f;
                ImGui::PushTextWrapPos(wrap_x);
                ImGui::TextUnformatted(t.text.c_str());
                ImGui::PopTextWrapPos();
                ImGui::PushStyleColor(ImGuiCol_Text, colors::text_dim);
                ImGui::SameLine(list_w - 30.0f);
                ImGui::TextUnformatted(format_age(t.created, now).c_str());
                ImGui::PopStyleColor();
                ImGui::Separator();
            }
            ImGui::EndChild();
        }
        ImGui::EndPopup();
    }

    ImGui::PopStyleVar(3);
    ImGui::PopStyleColor(2);
}

} // namespace fn_ui
