#include "game/ui/window_message.hpp"

#include <algorithm>
#include <utility>

#include "core/rendering/screen.hpp"
#include "core/rendering/text.hpp"
#include "utils/param.hpp"

WindowMessage::WindowMessage(
    std::shared_ptr<Text> text_renderer,
    std::string title,
    const Config& config)
    : text_renderer_(std::move(text_renderer)),
      config_(config),
      title_(std::move(title)),
      title_style_(Text::CENTER | Text::COLOR, config_.title_color, config_.title_color, 0, -2),
      text_style_(Text::CENTER | Text::COLOR, config_.text_color, config_.text_color, 0, -2) {
}

void WindowMessage::render() {
    if (!visible_) {
        return;
    }

    SDL_Renderer* renderer = Screen::get()->getRenderer();

    // Dibujar fondo con transparencia
    SDL_SetRenderDrawColor(renderer, config_.bg_color.r, config_.bg_color.g, config_.bg_color.b, config_.bg_color.a);
    SDL_RenderFillRect(renderer, &rect_);

    // Dibujar borde
    SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, config_.border_color.b, config_.border_color.a);
    SDL_RenderRect(renderer, &rect_);

    // Solo mostrar contenido si no estamos en animación de show/hide
    if (shouldShowContent()) {
        float current_y = rect_.y + config_.padding;
        float available_width = getAvailableTextWidth();

        // Dibujar título si existe
        if (!title_.empty()) {
            std::string visible_title = getTruncatedText(title_, available_width);
            if (!visible_title.empty()) {
                text_renderer_->writeStyle(
                    rect_.x + (rect_.w / 2.0F),
                    current_y,
                    visible_title,
                    title_style_);
            }
            current_y += text_renderer_->getCharacterSize() + config_.title_separator_spacing;

            // Línea separadora debajo del título (solo si hay título visible)
            if (!visible_title.empty()) {
                SDL_SetRenderDrawColor(renderer, config_.border_color.r, config_.border_color.g, config_.border_color.b, config_.border_color.a);
                SDL_RenderLine(renderer,
                    rect_.x + config_.padding,
                    current_y - (config_.title_separator_spacing / 2.0F),
                    rect_.x + rect_.w - config_.padding,
                    current_y - (config_.title_separator_spacing / 2.0F));
            }
        }

        // Dibujar textos
        for (const auto& text : texts_) {
            std::string visible_text = getTruncatedText(text, available_width);
            if (!visible_text.empty()) {
                text_renderer_->writeStyle(
                    rect_.x + (rect_.w / 2.0F),
                    current_y,
                    visible_text,
                    text_style_);
            }
            current_y += text_renderer_->getCharacterSize() + config_.line_spacing;
        }
    }
}

void WindowMessage::update(float delta_time) {
    // Actualizar animaciones
    if (show_hide_animation_.active || resize_animation_.active) {
        updateAnimation(delta_time);
    }
}

void WindowMessage::show() {
    if (visible_) {
        return;  // Ya visible
    }

    visible_ = true;
    ensureTextFits();

    // Detener cualquier animación anterior
    resize_animation_.stop();

    // Iniciar animación de mostrar desde tamaño 0
    show_hide_animation_.startShow(rect_.w, rect_.h);
    rect_.w = 0.0F;
    rect_.h = 0.0F;
    updatePosition();  // Reposicionar con tamaño 0
}

void WindowMessage::hide() {
    if (!visible_) {
        return;  // Ya oculto
    }

    // Detener cualquier animación anterior
    resize_animation_.stop();

    // Guardar el tamaño actual para la animación
    show_hide_animation_.target_width = rect_.w;
    show_hide_animation_.target_height = rect_.h;

    // Iniciar animación de ocultar hacia tamaño 0
    show_hide_animation_.startHide();
}

void WindowMessage::setTitle(const std::string& title) {
    title_ = title;
    triggerAutoResize();
}

void WindowMessage::setText(const std::string& text) {
    texts_.clear();
    texts_.push_back(text);
    triggerAutoResize();
}

void WindowMessage::setTexts(const std::vector<std::string>& texts) {
    texts_ = texts;
    triggerAutoResize();
}

void WindowMessage::addText(const std::string& text) {
    texts_.push_back(text);
    triggerAutoResize();
}

void WindowMessage::clearTexts() {
    texts_.clear();
    triggerAutoResize();
}

void WindowMessage::setPosition(float x, float y, PositionMode mode) {
    anchor_ = {.x = x, .y = y};
    position_mode_ = mode;
    updatePosition();
}

void WindowMessage::setSize(float width, float height) {
    rect_.w = width;
    rect_.h = height;
    updatePosition();  // Reposicionar después de cambiar el tamaño
}

void WindowMessage::centerOnScreen() {
    setPosition(getScreenWidth() / 2.0F, getScreenHeight() / 2.0F, PositionMode::CENTERED);
}

void WindowMessage::autoSize() {
    if (show_hide_animation_.active) {
        return;  // No redimensionar durante show/hide
    }

    if (resize_animation_.active) {
        resize_animation_.stop();  // Detener animación anterior
    }

    float old_width = rect_.w;
    float old_height = rect_.h;

    calculateAutoSize();

    // Solo animar si hay cambio en el tamaño y la ventana está visible
    if (visible_ && (old_width != rect_.w || old_height != rect_.h)) {
        resize_animation_.start(old_width, old_height, rect_.w, rect_.h);
        // Restaurar el tamaño anterior para que la animación funcione
        rect_.w = old_width;
        rect_.h = old_height;
    } else {
        updatePosition();  // Reposicionar después de ajustar el tamaño
    }
}

void WindowMessage::updateStyles() {
    title_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.title_color, config_.title_color, 0, -2);
    text_style_ = Text::Style(Text::CENTER | Text::COLOR, config_.text_color, config_.text_color, 0, -2);
}

void WindowMessage::updatePosition() {
    switch (position_mode_) {
        case PositionMode::CENTERED:
            rect_.x = anchor_.x - (rect_.w / 2.0F);
            rect_.y = anchor_.y - (rect_.h / 2.0F);
            break;
        case PositionMode::FIXED:
            rect_.x = anchor_.x;
            rect_.y = anchor_.y;
            break;
    }

    // Asegurar que la ventana esté dentro de los límites de la pantalla
    rect_.x = std::max(0.0F, std::min(rect_.x, getScreenWidth() - rect_.w));
    rect_.y = std::max(0.0F, std::min(rect_.y, getScreenHeight() - rect_.h));
}

void WindowMessage::ensureTextFits() {
    float required_width = calculateContentWidth() + (config_.padding * 2) + config_.text_safety_margin;
    float required_height = calculateContentHeight() + (config_.padding * 2) + config_.text_safety_margin;

    // Verificar si el tamaño actual es suficiente
    if (rect_.w < required_width || rect_.h < required_height) {
        autoSize();  // Recalcular tamaño automáticamente
    }
}

void WindowMessage::calculateAutoSize() {
    float content_width = calculateContentWidth();
    float content_height = calculateContentHeight();

    // Calcular dimensiones con padding y margen de seguridad
    rect_.w = content_width + (config_.padding * 2) + config_.text_safety_margin;
    rect_.h = content_height + (config_.padding * 2);

    // Aplicar límites mínimos
    rect_.w = std::max(rect_.w, config_.min_width);
    rect_.h = std::max(rect_.h, config_.min_height);

    // Aplicar límites máximos basados en el tamaño de pantalla
    float max_width = getScreenWidth() * config_.max_width_ratio;
    float max_height = getScreenHeight() * config_.max_height_ratio;

    rect_.w = std::min(rect_.w, max_width);
    rect_.h = std::min(rect_.h, max_height);
}

auto WindowMessage::calculateContentHeight() const -> float {
    float height = 0;

    // Altura del título
    if (!title_.empty()) {
        height += text_renderer_->getCharacterSize() + config_.title_separator_spacing;
    }

    // Altura de los textos
    if (!texts_.empty()) {
        height += (texts_.size() * text_renderer_->getCharacterSize());
        if (texts_.size() > 1) {
            height += ((texts_.size() - 1) * config_.line_spacing);
        }
    }

    return height;
}

auto WindowMessage::calculateContentWidth() const -> float {
    float max_width = config_.min_width - (config_.padding * 2);  // Ancho mínimo sin padding

    // Ancho del título
    if (!title_.empty()) {
        float title_width = text_renderer_->length(title_, -2);
        max_width = std::max(max_width, title_width);
    }

    // Ancho de los textos
    for (const auto& text : texts_) {
        float text_width = text_renderer_->length(text, -2);
        max_width = std::max(max_width, text_width);
    }

    return max_width;
}

auto WindowMessage::getScreenWidth() -> float {
    return param.game.width;
}

auto WindowMessage::getScreenHeight() -> float {
    return param.game.height;
}

void WindowMessage::triggerAutoResize() {
    if (auto_resize_enabled_) {
        autoSize();
    }
}

void WindowMessage::updateAnimation(float delta_time) {
    if (show_hide_animation_.active) {
        updateShowHideAnimation(delta_time);
    }

    if (resize_animation_.active) {
        updateResizeAnimation(delta_time);
    }
}

void WindowMessage::updateShowHideAnimation(float delta_time) {
    if (!show_hide_animation_.active) {
        return;
    }

    show_hide_animation_.elapsed += delta_time;

    if (show_hide_animation_.isFinished(config_.animation_duration)) {
        // Animación terminada
        if (show_hide_animation_.type == ShowHideAnimation::Type::SHOWING) {
            // Mostrar completado
            rect_.w = show_hide_animation_.target_width;
            rect_.h = show_hide_animation_.target_height;
        } else if (show_hide_animation_.type == ShowHideAnimation::Type::HIDING) {
            // Ocultar completado
            rect_.w = 0.0F;
            rect_.h = 0.0F;
            visible_ = false;
        }

        show_hide_animation_.stop();
        updatePosition();
    } else {
        // Interpolar el tamaño
        float progress = easeOut(show_hide_animation_.getProgress(config_.animation_duration));

        if (show_hide_animation_.type == ShowHideAnimation::Type::SHOWING) {
            // Crecer desde 0 hasta el tamaño objetivo
            rect_.w = show_hide_animation_.target_width * progress;
            rect_.h = show_hide_animation_.target_height * progress;
        } else if (show_hide_animation_.type == ShowHideAnimation::Type::HIDING) {
            // Decrecer desde el tamaño actual hasta 0
            rect_.w = show_hide_animation_.target_width * (1.0F - progress);
            rect_.h = show_hide_animation_.target_height * (1.0F - progress);
        }

        updatePosition();  // Mantener la posición centrada durante la animación
    }
}

void WindowMessage::updateResizeAnimation(float delta_time) {
    if (!resize_animation_.active) {
        return;
    }

    resize_animation_.elapsed += delta_time;

    if (resize_animation_.isFinished(config_.animation_duration)) {
        // Animación terminada
        rect_.w = resize_animation_.target_width;
        rect_.h = resize_animation_.target_height;
        resize_animation_.stop();
        updatePosition();
    } else {
        // Interpolar el tamaño
        float progress = easeOut(resize_animation_.getProgress(config_.animation_duration));

        rect_.w = resize_animation_.start_width +
            ((resize_animation_.target_width - resize_animation_.start_width) * progress);
        rect_.h = resize_animation_.start_height +
            ((resize_animation_.target_height - resize_animation_.start_height) * progress);

        updatePosition();  // Mantener la posición centrada durante la animación
    }
}

auto WindowMessage::shouldShowContent() const -> bool {
    // No mostrar contenido durante animaciones de show/hide
    return !show_hide_animation_.active;
}

auto WindowMessage::easeOut(float t) -> float {
    // Función de suavizado ease-out cuadrática
    return 1.0F - ((1.0F - t) * (1.0F - t));
}

auto WindowMessage::getAvailableTextWidth() const -> float {
    // Ancho disponible = ancho total - padding en ambos lados
    return rect_.w - (config_.padding * 2.0F);
}

auto WindowMessage::getTruncatedText(const std::string& text, float available_width) const -> std::string {
    if (text.empty()) {
        return text;
    }

    // Si el texto completo cabe, devolverlo tal como está
    int text_width = text_renderer_->length(text, -2);
    if (text_width <= available_width) {
        return text;
    }

    // Si no hay espacio suficiente, devolver string vacío
    if (available_width < 10.0F) {  // Mínimo espacio para al menos un carácter
        return "";
    }

    // Buscar cuántos caracteres caben usando búsqueda binaria
    int left = 0;
    int right = text.length();
    int best_length = 0;

    while (left <= right) {
        int mid = (left + right) / 2;
        std::string partial = text.substr(0, mid);
        int partial_width = text_renderer_->length(partial, -2);

        if (partial_width <= available_width) {
            best_length = mid;
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    return text.substr(0, best_length);
}