#include "notifier.hpp"
#include "text/textrenderer.hpp"
#include "theme_manager.hpp"
#include "defines.hpp"
#include "utils/easing_functions.hpp"
#include <SDL3/SDL.h>

// ============================================================================
// HELPER: Obtener viewport en coordenadas físicas (no lógicas)
// ============================================================================
// SDL_GetRenderViewport() devuelve coordenadas LÓGICAS cuando hay presentación
// lógica activa. Para obtener coordenadas FÍSICAS, necesitamos deshabilitar
// temporalmente la presentación lógica.
static SDL_Rect getPhysicalViewport(SDL_Renderer* renderer) {
    // Guardar estado actual de presentación lógica
    int logical_w = 0, logical_h = 0;
    SDL_RendererLogicalPresentation presentation_mode;
    SDL_GetRenderLogicalPresentation(renderer, &logical_w, &logical_h, &presentation_mode);

    // Deshabilitar presentación lógica temporalmente
    SDL_SetRenderLogicalPresentation(renderer, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);

    // Obtener viewport en coordenadas físicas (píxeles reales)
    SDL_Rect physical_viewport;
    SDL_GetRenderViewport(renderer, &physical_viewport);

    // Restaurar presentación lógica
    SDL_SetRenderLogicalPresentation(renderer, logical_w, logical_h, presentation_mode);

    return physical_viewport;
}

Notifier::Notifier()
    : renderer_(nullptr)
    , text_renderer_(nullptr)
    , theme_manager_(nullptr)
    , window_width_(0)
    , window_height_(0)
    , current_notification_(nullptr) {
}

Notifier::~Notifier() {
    clear();
}

bool Notifier::init(SDL_Renderer* renderer, TextRenderer* text_renderer, ThemeManager* theme_manager, int window_width, int window_height) {
    renderer_ = renderer;
    text_renderer_ = text_renderer;
    theme_manager_ = theme_manager;
    window_width_ = window_width;
    window_height_ = window_height;
    return (renderer_ != nullptr && text_renderer_ != nullptr && theme_manager_ != nullptr);
}

void Notifier::updateWindowSize(int window_width, int window_height) {
    window_width_ = window_width;
    window_height_ = window_height;
}

void Notifier::show(const std::string& text, Uint64 duration) {
    if (text.empty()) {
        return;
    }

    // Usar duración default si no se especifica
    if (duration == 0) {
        duration = NOTIFICATION_DURATION;
    }

    // NUEVO: Limpiar notificación actual y cola (solo mostrar la última)
    // Si hay una notificación activa, destruirla inmediatamente
    current_notification_.reset();

    // Vaciar cola completa (descartar notificaciones pendientes)
    while (!notification_queue_.empty()) {
        notification_queue_.pop();
    }

    // Crear nueva notificación
    Notification notif;
    notif.text = text;
    notif.created_time = SDL_GetTicks();
    notif.duration = duration;
    notif.state = NotificationState::SLIDING_IN;
    notif.alpha = 1.0f;
    notif.y_offset = -50.0f;  // Comienza 50px arriba (fuera de pantalla)
    // NOTA: Los colores se obtienen dinámicamente desde ThemeManager en render()

    // Activar inmediatamente como notificación actual (sin esperar en cola)
    current_notification_ = std::make_unique<Notification>(notif);
}

void Notifier::update(Uint64 current_time) {
    // Activar siguiente notificación si no hay ninguna activa
    if (!current_notification_ && !notification_queue_.empty()) {
        processQueue();
    }

    // Actualizar notificación actual
    if (current_notification_) {
        Uint64 elapsed = current_time - current_notification_->created_time;

        switch (current_notification_->state) {
            case NotificationState::SLIDING_IN: {
                // Animación de entrada (NOTIFICATION_SLIDE_TIME ms)
                if (elapsed < NOTIFICATION_SLIDE_TIME) {
                    float progress = static_cast<float>(elapsed) / static_cast<float>(NOTIFICATION_SLIDE_TIME);
                    float eased = Easing::easeOutBack(progress);  // Efecto con ligero overshoot
                    current_notification_->y_offset = -50.0f + (50.0f * eased);  // De -50 a 0
                } else {
                    // Transición a VISIBLE
                    current_notification_->y_offset = 0.0f;
                    current_notification_->state = NotificationState::VISIBLE;
                }
                break;
            }

            case NotificationState::VISIBLE: {
                // Esperar hasta que se cumpla la duración
                Uint64 visible_time = current_notification_->duration - NOTIFICATION_FADE_TIME;
                if (elapsed >= visible_time) {
                    current_notification_->state = NotificationState::FADING_OUT;
                }
                break;
            }

            case NotificationState::FADING_OUT: {
                // Animación de salida (NOTIFICATION_FADE_TIME ms)
                Uint64 fade_start = current_notification_->duration - NOTIFICATION_FADE_TIME;
                Uint64 fade_elapsed = elapsed - fade_start;

                if (fade_elapsed < NOTIFICATION_FADE_TIME) {
                    float progress = static_cast<float>(fade_elapsed) / static_cast<float>(NOTIFICATION_FADE_TIME);
                    float eased = Easing::easeInQuad(progress);  // Fade suave
                    current_notification_->alpha = 1.0f - eased;
                } else {
                    // Transición a DONE
                    current_notification_->alpha = 0.0f;
                    current_notification_->state = NotificationState::DONE;
                }
                break;
            }

            case NotificationState::DONE: {
                // Eliminar notificación actual
                current_notification_.reset();
                break;
            }
        }
    }
}

void Notifier::render() {
    if (!current_notification_ || !text_renderer_ || !renderer_ || !theme_manager_) {
        return;
    }

    // Obtener colores DINÁMICOS desde ThemeManager (incluye LERP automático)
    int text_r, text_g, text_b;
    theme_manager_->getCurrentThemeTextColor(text_r, text_g, text_b);
    SDL_Color text_color = {
        static_cast<Uint8>(text_r),
        static_cast<Uint8>(text_g),
        static_cast<Uint8>(text_b),
        static_cast<Uint8>(current_notification_->alpha * 255.0f)
    };

    int bg_r, bg_g, bg_b;
    theme_manager_->getCurrentNotificationBackgroundColor(bg_r, bg_g, bg_b);
    SDL_Color bg_color = {
        static_cast<Uint8>(bg_r),
        static_cast<Uint8>(bg_g),
        static_cast<Uint8>(bg_b),
        255
    };

    // Calcular dimensiones del texto en píxeles FÍSICOS
    // IMPORTANTE: Usar getTextWidthPhysical() en lugar de getTextWidth()
    // para obtener el ancho REAL de la fuente (sin escalado lógico)
    int text_width = text_renderer_->getTextWidthPhysical(current_notification_->text.c_str());
    int text_height = text_renderer_->getTextHeight();

    // Calcular dimensiones del fondo con padding (en píxeles físicos)
    int bg_width = text_width + (NOTIFICATION_PADDING * 2);
    int bg_height = text_height + (NOTIFICATION_PADDING * 2);

    // Obtener viewport FÍSICO (píxeles reales, no lógicos)
    // CRÍTICO: En F3, SDL_GetRenderViewport() devuelve coordenadas LÓGICAS,
    // pero printAbsolute() trabaja en píxeles FÍSICOS. Usar helper para obtener
    // viewport en coordenadas físicas.
    SDL_Rect physical_viewport = getPhysicalViewport(renderer_);

    // Centrar en el viewport físico (coordenadas relativas al viewport)
    int x = (physical_viewport.w / 2) - (bg_width / 2);
    int y = NOTIFICATION_TOP_MARGIN + static_cast<int>(current_notification_->y_offset);

    // Renderizar fondo semitransparente (con bypass de presentación lógica)
    float bg_alpha = current_notification_->alpha * NOTIFICATION_BG_ALPHA;
    renderBackground(x, y, bg_width, bg_height, bg_alpha, bg_color);

    // Renderizar texto con alpha usando printAbsolute (tamaño físico fijo)
    int text_x = x + NOTIFICATION_PADDING;
    int text_y = y + NOTIFICATION_PADDING;

    // printAbsolute() ya maneja el bypass de presentación lógica internamente
    text_renderer_->printAbsolute(text_x, text_y, current_notification_->text.c_str(), text_color);
}

void Notifier::renderBackground(int x, int y, int width, int height, float alpha, SDL_Color bg_color) {
    if (!renderer_) {
        return;
    }

    // Obtener viewport ANTES de deshabilitar presentación lógica
    // En modo letterbox (F3), SDL crea un viewport con offset para centrar la imagen
    SDL_Rect viewport;
    SDL_GetRenderViewport(renderer_, &viewport);

    // Crear rectángulo para el fondo (en coordenadas físicas)
    // Aplicar offset del viewport para que el fondo se pinte dentro del área visible
    SDL_FRect bg_rect;
    bg_rect.x = static_cast<float>(x + viewport.x);
    bg_rect.y = static_cast<float>(y + viewport.y);
    bg_rect.w = static_cast<float>(width);
    bg_rect.h = static_cast<float>(height);

    // Color del tema con alpha
    Uint8 bg_alpha = static_cast<Uint8>(alpha * 255.0f);
    SDL_SetRenderDrawColor(renderer_, bg_color.r, bg_color.g, bg_color.b, bg_alpha);

    // Habilitar blending para transparencia
    SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_BLEND);

    // CRÍTICO: Deshabilitar presentación lógica para renderizar en píxeles físicos absolutos
    // (igual que printAbsolute() en TextRenderer)
    int logical_w = 0, logical_h = 0;
    SDL_RendererLogicalPresentation presentation_mode;
    SDL_GetRenderLogicalPresentation(renderer_, &logical_w, &logical_h, &presentation_mode);

    // Renderizar sin presentación lógica (coordenadas físicas absolutas con offset de viewport)
    SDL_SetRenderLogicalPresentation(renderer_, 0, 0, SDL_LOGICAL_PRESENTATION_DISABLED);
    SDL_RenderFillRect(renderer_, &bg_rect);

    // Restaurar presentación lógica
    SDL_SetRenderLogicalPresentation(renderer_, logical_w, logical_h, presentation_mode);

    // Restaurar blend mode
    SDL_SetRenderDrawBlendMode(renderer_, SDL_BLENDMODE_NONE);
}

bool Notifier::isActive() const {
    return (current_notification_ != nullptr);
}

void Notifier::clear() {
    // Vaciar cola
    while (!notification_queue_.empty()) {
        notification_queue_.pop();
    }
    // Eliminar notificación actual
    current_notification_.reset();
}

void Notifier::processQueue() {
    if (notification_queue_.empty()) {
        return;
    }

    // Sacar siguiente notificación de la cola
    Notification next_notif = notification_queue_.front();
    notification_queue_.pop();

    // Activarla como notificación actual
    current_notification_ = std::make_unique<Notification>(next_notif);
}
