#include "ui_manager.hpp"

#include <SDL3/SDL.h>

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

#include "ball.hpp"                 // for Ball
#include "defines.hpp"              // for TEXT_DURATION, NOTIFICATION_DURATION, AppMode, SimulationMode
#include "engine.hpp"               // for Engine (info de sistema)
#include "help_overlay.hpp"         // for HelpOverlay
#include "notifier.hpp"             // for Notifier
#include "scene/scene_manager.hpp"  // for SceneManager
#include "shapes/shape.hpp"         // for Shape
#include "text/textrenderer.hpp"    // for TextRenderer
#include "theme_manager.hpp"        // for ThemeManager

// ============================================================================
// 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 auto getPhysicalViewport(SDL_Renderer* renderer) -> SDL_Rect {
    // Guardar estado actual de presentación lógica
    int logical_w = 0;
    int 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;
}

UIManager::UIManager()
    : text_renderer_debug_(nullptr),
      text_renderer_notifier_(nullptr),
      notifier_(nullptr),
      help_overlay_(nullptr),
      show_debug_(false),
      fps_last_time_(0),
      fps_frame_count_(0),
      fps_current_(0),
      fps_text_("FPS: 0"),
      vsync_text_("VSYNC ON"),
      renderer_(nullptr),
      theme_manager_(nullptr),
      physical_window_width_(0),
      physical_window_height_(0),
      logical_window_width_(0),
      logical_window_height_(0),
      current_font_size_(18) {  // Tamaño por defecto (medium)
}

UIManager::~UIManager() {
    // Limpieza: Los objetos creados con new deben ser eliminados
    delete text_renderer_debug_;
    delete text_renderer_notifier_;
    delete notifier_;
    delete help_overlay_;
}

void UIManager::initialize(SDL_Renderer* renderer, ThemeManager* theme_manager, int physical_width, int physical_height, int logical_width, int logical_height) {
    delete text_renderer_debug_;
    text_renderer_debug_ = nullptr;
    delete text_renderer_notifier_;
    text_renderer_notifier_ = nullptr;
    delete notifier_;
    notifier_ = nullptr;
    delete help_overlay_;
    help_overlay_ = nullptr;

    renderer_ = renderer;
    theme_manager_ = theme_manager;
    physical_window_width_ = physical_width;
    physical_window_height_ = physical_height;
    logical_window_width_ = logical_width;
    logical_window_height_ = logical_height;

    // Calcular tamaño de fuente apropiado según dimensiones LÓGICAS (sin zoom)
    current_font_size_ = calculateFontSize(logical_height);

    // Crear renderers de texto
    text_renderer_debug_ = new TextRenderer();
    text_renderer_notifier_ = new TextRenderer();

    // Inicializar renderers con tamaño dinámico
    text_renderer_debug_->init(renderer, APP_FONT, std::max(9, current_font_size_ - 2), true);
    text_renderer_notifier_->init(renderer, APP_FONT, current_font_size_, true);

    // Crear y configurar sistema de notificaciones
    notifier_ = new Notifier();
    notifier_->init(renderer, text_renderer_notifier_, theme_manager_, physical_width, physical_height);

    // Crear y configurar sistema de ayuda (overlay)
    help_overlay_ = new HelpOverlay();
    help_overlay_->initialize(renderer, theme_manager_, physical_width, physical_height, std::max(9, current_font_size_ - 1));

    // Inicializar FPS counter
    fps_last_time_ = SDL_GetTicks();
    fps_frame_count_ = 0;
    fps_current_ = 0;
}

void UIManager::update(Uint64 current_time, float delta_time) {
    // Calcular FPS
    fps_frame_count_++;
    if (current_time - fps_last_time_ >= 1000) {  // Actualizar cada segundo
        fps_current_ = fps_frame_count_;
        fps_frame_count_ = 0;
        fps_last_time_ = current_time;
        fps_text_ = "FPS: " + std::to_string(fps_current_);
    }

    // Actualizar sistema de notificaciones
    notifier_->update(current_time);
}

void UIManager::render(SDL_Renderer* renderer,
    const Engine* engine,
    const SceneManager* scene_manager,
    SimulationMode current_mode,
    AppMode current_app_mode,
    const Shape* active_shape,
    float shape_convergence,
    int physical_width,
    int physical_height,
    int current_screen_width) {
    // Actualizar dimensiones físicas (puede cambiar en fullscreen)
    physical_window_width_ = physical_width;
    physical_window_height_ = physical_height;

    // Renderizar debug HUD si está activo
    if (show_debug_) {
        renderDebugHUD(engine, scene_manager, current_mode, current_app_mode, active_shape, shape_convergence);
    }

    // Renderizar notificaciones (siempre al final, sobre todo lo demás)
    notifier_->render();

    // Renderizar ayuda (siempre última, sobre todo incluso notificaciones)
    if (help_overlay_ != nullptr) {
        help_overlay_->render(renderer);
    }
}

void UIManager::toggleDebug() {
    show_debug_ = !show_debug_;
}

void UIManager::toggleHelp() {
    if (help_overlay_ != nullptr) {
        help_overlay_->toggle();
    }
}

void UIManager::showNotification(const std::string& text, Uint64 duration) {
    if (duration == 0) {
        duration = NOTIFICATION_DURATION;
    }
    notifier_->show(text, duration);
}

void UIManager::updateVSyncText(bool enabled) {
    vsync_text_ = enabled ? "V-Sync: On" : "V-Sync: Off";
}

void UIManager::updatePhysicalWindowSize(int width, int height, int logical_height) {
    physical_window_width_ = width;
    physical_window_height_ = height;

    // Actualizar altura lógica si se proporciona (ej. al entrar/salir de F4)
    if (logical_height > 0) {
        logical_window_height_ = logical_height;
    }

    // Calcular nuevo tamaño de fuente apropiado basado en altura LÓGICA
    // (las dimensiones lógicas no cambian con zoom, solo con cambios explícitos de resolución)
    int new_font_size = calculateFontSize(logical_window_height_);

    // Si el tamaño cambió, reinicializar todos los text renderers
    if (new_font_size != current_font_size_) {
        current_font_size_ = new_font_size;

        // Reinicializar text renderers con nuevo tamaño
        if (text_renderer_debug_ != nullptr) {
            text_renderer_debug_->reinitialize(std::max(9, current_font_size_ - 2));
        }
        if (text_renderer_notifier_ != nullptr) {
            text_renderer_notifier_->reinitialize(current_font_size_);
        }
    }

    // Actualizar help overlay con font size actual Y nuevas dimensiones (atómicamente)
    if (help_overlay_ != nullptr) {
        help_overlay_->updateAll(std::max(9, current_font_size_ - 1), width, height);
    }

    // Actualizar otros componentes de UI con nuevas dimensiones
    notifier_->updateWindowSize(width, height);
}

// === Métodos privados ===

void UIManager::renderDebugHUD(const Engine* engine,  // NOLINT(readability-function-cognitive-complexity)
    const SceneManager* scene_manager,
    SimulationMode current_mode,
    AppMode current_app_mode,
    const Shape* active_shape,
    float shape_convergence) {
    int line_height = text_renderer_debug_->getTextHeight();
    int margin = 8;
    SDL_Rect physical_viewport = getPhysicalViewport(renderer_);

    // --- Construir strings ---

    std::string appmode_text;
    if (current_app_mode == AppMode::LOGO) {
        appmode_text = "AppMode: LOGO";
    } else if (current_app_mode == AppMode::DEMO) {
        appmode_text = "AppMode: DEMO";
    } else if (current_app_mode == AppMode::DEMO_LITE) {
        appmode_text = "AppMode: DEMO LITE";
    } else {
        appmode_text = "AppMode: SANDBOX";
    }

    std::string simmode_text;
    if (current_mode == SimulationMode::PHYSICS) {
        simmode_text = "SimMode: PHYSICS";
    } else if (current_mode == SimulationMode::SHAPE) {
        if (active_shape != nullptr) {
            simmode_text = std::string("SimMode: SHAPE (") + active_shape->getName() + ")";
        } else {
            simmode_text = "SimMode: SHAPE";
        }
    } else if (current_mode == SimulationMode::BOIDS) {
        simmode_text = "SimMode: BOIDS";
    }

    std::string sprite_name = engine->getCurrentTextureName();
    std::ranges::transform(sprite_name, sprite_name.begin(), ::toupper);
    std::string sprite_text = "Sprite: " + sprite_name;

    size_t ball_count = scene_manager->getBallCount();
    std::string balls_text;
    if (ball_count >= 1000) {
        std::string count_str = std::to_string(ball_count);
        std::string formatted;
        int digits = static_cast<int>(count_str.length());
        for (int i = 0; i < digits; i++) {
            if (i > 0 && (digits - i) % 3 == 0) {
                formatted += ',';
            }
            formatted += count_str[i];
        }
        balls_text = "Balls: " + formatted;
    } else {
        balls_text = "Balls: " + std::to_string(ball_count);
    }

    int max_auto_idx = engine->getMaxAutoScenario();
    int max_auto_balls = BALL_COUNT_SCENARIOS[max_auto_idx];
    if (engine->isCustomAutoAvailable() && engine->getCustomScenarioBalls() > max_auto_balls) {
        max_auto_balls = engine->getCustomScenarioBalls();
    }
    std::string max_auto_text;
    if (max_auto_balls >= 1000) {
        std::string count_str = std::to_string(max_auto_balls);
        std::string formatted;
        int digits = static_cast<int>(count_str.length());
        for (int i = 0; i < digits; i++) {
            if (i > 0 && (digits - i) % 3 == 0) {
                formatted += ',';
            }
            formatted += count_str[i];
        }
        max_auto_text = "Auto max: " + formatted;
    } else {
        max_auto_text = "Auto max: " + std::to_string(max_auto_balls);
    }

    std::string scaling_text;
    if (engine->getFullscreenEnabled() || engine->getRealFullscreenEnabled()) {
        ScalingMode scaling = engine->getCurrentScalingMode();
        if (scaling == ScalingMode::INTEGER) {
            scaling_text = "Scaling: INTEGER";
        } else if (scaling == ScalingMode::LETTERBOX) {
            scaling_text = "Scaling: LETTERBOX";
        } else if (scaling == ScalingMode::STRETCH) {
            scaling_text = "Scaling: STRETCH";
        }
    } else {
        scaling_text = "Scaling: WINDOWED";
    }

    std::string phys_res_text = "Physical: " + std::to_string(physical_window_width_) + "x" + std::to_string(physical_window_height_);
    std::string logic_res_text = "Logical: " + std::to_string(engine->getCurrentScreenWidth()) + "x" + std::to_string(engine->getCurrentScreenHeight());

    std::string refresh_text;
    int num_displays = 0;
    SDL_DisplayID* displays = SDL_GetDisplays(&num_displays);
    if ((displays != nullptr) && num_displays > 0) {
        const auto* dm = SDL_GetCurrentDisplayMode(displays[0]);
        if (dm != nullptr) {
            refresh_text = "Refresh: " + std::to_string(static_cast<int>(dm->refresh_rate)) + " Hz";
        } else {
            refresh_text = "Refresh: N/A";
        }
        SDL_free(displays);
    } else {
        refresh_text = "Refresh: N/A";
    }

    std::string theme_text = std::string("Theme: ") + theme_manager_->getCurrentThemeNameEN();

    Uint64 ticks_ms = SDL_GetTicks();
    Uint64 total_secs = ticks_ms / 1000;
    int hh = static_cast<int>(total_secs / 3600);
    int mm = static_cast<int>((total_secs % 3600) / 60);
    int ss = static_cast<int>(total_secs % 60);
    std::array<char, 32> elapsed_buf{};
    SDL_snprintf(elapsed_buf.data(), elapsed_buf.size(), "Elapsed: %02d:%02d:%02d", hh, mm, ss);
    std::string elapsed_text(elapsed_buf.data());

    // --- Construir vector de líneas en orden ---
    std::vector<std::string> lines;
    lines.push_back(fps_text_);
    lines.push_back(appmode_text);
    lines.push_back(simmode_text);
    lines.push_back(sprite_text);
    lines.push_back(balls_text);
    lines.push_back(max_auto_text);
    lines.push_back(vsync_text_);
    lines.push_back(scaling_text);
    lines.push_back(phys_res_text);
    lines.push_back(logic_res_text);
    lines.push_back(refresh_text);
    lines.push_back(theme_text);
    std::string postfx_text;
    if (!engine->isPostFXEnabled()) {
        postfx_text = "PostFX: OFF";
    } else {
        static constexpr std::array<const char*, 4> PRESET_NAMES = {
            "Vinyeta",
            "Scanlines",
            "Cromatica",
            "Complet"};
        int mode = engine->getPostFXMode();
        std::array<char, 64> buf{};
        SDL_snprintf(buf.data(), buf.size(), "PostFX: %s [V:%.2f C:%.2f S:%.2f]", PRESET_NAMES[mode], engine->getPostFXVignette(), engine->getPostFXChroma(), engine->getPostFXScanline());
        postfx_text = buf.data();
    }
    lines.push_back(postfx_text);
    lines.push_back(elapsed_text);

    const Ball* first_ball = scene_manager->getFirstBall();
    if (first_ball != nullptr) {
        lines.push_back("VelX: " + std::to_string(static_cast<int>(first_ball->getVelocityX())));
        lines.push_back("VelY: " + std::to_string(static_cast<int>(first_ball->getVelocityY())));
        SDL_FRect pos = first_ball->getPosition();
        lines.push_back("Pos: (" + std::to_string(static_cast<int>(pos.x)) + ", " + std::to_string(static_cast<int>(pos.y)) + ")");
        lines.push_back("Gravity: " + std::to_string(static_cast<int>(first_ball->getGravityForce())));
        lines.emplace_back(first_ball->isOnSurface() ? "Surface: YES" : "Surface: NO");
        lines.push_back("Loss: " + std::to_string(first_ball->getLossCoefficient()).substr(0, 4));
        lines.push_back("Dir: " + gravityDirectionToString(static_cast<int>(scene_manager->getCurrentGravity())));
    }

    if (current_app_mode == AppMode::LOGO && current_mode == SimulationMode::SHAPE) {
        int convergence_percent = static_cast<int>(shape_convergence * 100.0f);
        lines.push_back("Convergence: " + std::to_string(convergence_percent) + "%");
    }

    // --- Render con desbordamiento a segunda columna ---
    int max_lines = (physical_viewport.h - 2 * margin) / line_height;
    max_lines = std::max(max_lines, 1);
    int col_width = physical_viewport.w / 2;

    for (int i = 0; i < static_cast<int>(lines.size()); i++) {
        int col = i / max_lines;
        int row = i % max_lines;
        int x = margin + (col * col_width);
        int y = margin + (row * line_height);
        text_renderer_debug_->printAbsoluteShadowed(x, y, lines[i].c_str());
    }
}

auto UIManager::gravityDirectionToString(int direction) -> std::string {
    switch (direction) {
        case 0:
            return "Abajo";  // DOWN
        case 1:
            return "Arriba";  // UP
        case 2:
            return "Izquierda";  // LEFT
        case 3:
            return "Derecha";  // RIGHT
        default:
            return "Desconocida";
    }
}

auto UIManager::calculateFontSize(int logical_height) -> int {
    // Escalado híbrido basado en ALTURA LÓGICA (resolución interna, sin zoom)
    // Esto asegura que el tamaño de fuente sea consistente independientemente del zoom de ventana
    // - Proporcional en extremos (muy bajo/alto)
    // - Escalonado en rango medio (estabilidad)

    int font_size = 14;  // Default fallback

    if (logical_height < 300) {
        // Rango bajo: proporcional (240px→9.6, 280px→11.2)
        font_size = logical_height / 25;
    } else if (logical_height < 380) {
        // Rango muy bajo (300-379px) → 10px (crítico para 640x360)
        font_size = 10;
    } else if (logical_height < 500) {
        // Rango medio-bajo (380-499px) → 12px
        font_size = 12;
    } else if (logical_height < 700) {
        // Rango medio (500-699px) → 14px
        font_size = 14;
    } else if (logical_height < 900) {
        // Rango medio-alto (700-899px) → 18px
        font_size = 18;
    } else if (logical_height < 1200) {
        // Rango alto (900-1199px): 900→22, 1080→27, 1199→29
        font_size = logical_height / 40;
    } else if (logical_height < 1600) {
        // Rango muy alto (1200-1599px): 1200→25, 1440→30
        font_size = logical_height / 48;
    } else {
        // Rango ultra (>=1600px): 1600→26, 2000→33, 2160→36
        font_size = logical_height / 60;
    }

    // Aplicar límites: mínimo 9px, máximo 72px
    font_size = std::max(font_size, 9);
    font_size = std::min(font_size, 72);

    return font_size;
}
