#ifdef _DEBUG

#include "game/editor/tile_picker.hpp"

#include <algorithm>  // Para std::clamp, std::min

#include "core/rendering/screen.hpp"          // Para Screen
#include "core/rendering/surface.hpp"         // Para Surface
#include "core/resources/resource_cache.hpp"  // Para Resource::Cache
#include "utils/defines.hpp"                  // Para Tile::SIZE, PlayArea
#include "utils/utils.hpp"

// Margen del borde alrededor del tileset (en pixels)
static constexpr int BORDER_PAD = 3;

// Abre el picker con un tileset
void TilePicker::open(const std::string& tileset_name, int current_tile, int bg_color, int source_color, int target_color, int tile_spacing_in, int tile_spacing_out, bool allow_rect_selection) {
    tileset_ = Resource::Cache::get()->getSurface(tileset_name);
    if (!tileset_) {
        open_ = false;
        return;
    }

    spacing_in_ = tile_spacing_in;
    spacing_out_ = tile_spacing_out;
    allow_rect_ = allow_rect_selection;
    selecting_rect_ = false;
    rect_start_tile_ = -1;
    last_valid_hover_ = -1;

    // Calcular dimensiones del tileset en tiles (teniendo en cuenta spacing de entrada)
    int src_cell = Tile::SIZE + spacing_in_;
    tileset_width_ = static_cast<int>(tileset_->getWidth()) / src_cell;
    tileset_height_ = static_cast<int>(tileset_->getHeight()) / src_cell;
    // Corregir si el último tile cabe sin spacing
    if (tileset_width_ == 0 && tileset_->getWidth() >= Tile::SIZE) { tileset_width_ = 1; }
    if (tileset_height_ == 0 && tileset_->getHeight() >= Tile::SIZE) { tileset_height_ = 1; }

    current_tile_ = current_tile;
    hover_tile_ = -1;
    scroll_y_ = 0;

    // Dimensiones de salida (con spacing visual entre tiles)
    int out_cell = Tile::SIZE + spacing_out_;
    int display_w = (tileset_width_ * out_cell) - spacing_out_;  // Sin trailing
    int display_h = (tileset_height_ * out_cell) - spacing_out_;

    // Frame: display + borde
    int frame_w = display_w + (BORDER_PAD * 2);
    int frame_h = display_h + (BORDER_PAD * 2);
    frame_surface_ = std::make_shared<Surface>(frame_w, frame_h);

    // Componer: fondo + borde + tiles uno a uno
    {
        auto prev = Screen::get()->getRendererSurface();
        Screen::get()->setRendererSurface(frame_surface_);

        Uint8 fill_color = (bg_color >= 0) ? static_cast<Uint8>(bg_color) : 0;
        frame_surface_->clear(fill_color);

        // Borde doble
        SDL_FRect outer = {.x = 0, .y = 0, .w = static_cast<float>(frame_w), .h = static_cast<float>(frame_h)};
        frame_surface_->drawRectBorder(&outer, 15);
        SDL_FRect inner = {.x = 1, .y = 1, .w = static_cast<float>(frame_w - 2), .h = static_cast<float>(frame_h - 2)};
        frame_surface_->drawRectBorder(&inner, 14);

        // Renderizar cada tile individualmente
        constexpr auto TS = static_cast<float>(Tile::SIZE);
        for (int row = 0; row < tileset_height_; ++row) {
            for (int col = 0; col < tileset_width_; ++col) {
                // Fuente: posición en el tileset original
                SDL_FRect src = {
                    .x = static_cast<float>(col * src_cell),
                    .y = static_cast<float>(row * src_cell),
                    .w = TS,
                    .h = TS};

                // Destino: posición en el frame con spacing de salida
                int dst_x = BORDER_PAD + (col * out_cell);
                int dst_y = BORDER_PAD + (row * out_cell);

                if (source_color >= 0 && target_color >= 0) {
                    tileset_->renderWithColorReplace(dst_x, dst_y, static_cast<Uint8>(source_color), static_cast<Uint8>(target_color), &src);
                } else {
                    SDL_FRect dst = {.x = static_cast<float>(dst_x), .y = static_cast<float>(dst_y), .w = TS, .h = TS};
                    tileset_->render(&src, &dst);
                }
            }
        }

        Screen::get()->setRendererSurface(prev);
    }

    // Centrar en el play area
    offset_x_ = (PlayArea::WIDTH - frame_w) / 2;
    int offset_y = (PlayArea::HEIGHT - frame_h) / 2;
    offset_y = std::max(offset_y, 0);

    visible_height_ = PlayArea::HEIGHT;

    frame_dst_ = {.x = static_cast<float>(offset_x_), .y = static_cast<float>(offset_y), .w = static_cast<float>(frame_w), .h = static_cast<float>(frame_h)};

    // Si el frame es más alto que el play area, necesitará scroll
    if (frame_h > visible_height_) {
        frame_dst_.y = 0;
        if (current_tile_ >= 0) {
            int tile_row = current_tile_ / tileset_width_;
            int tile_y_px = tile_row * out_cell;
            if (tile_y_px > visible_height_ / 2) {
                scroll_y_ = tile_y_px - visible_height_ / 2;
            }
        }
    }

    open_ = true;
    updateMousePosition();
}

// Cierra el picker
void TilePicker::close() {
    open_ = false;
    tileset_.reset();
    frame_surface_.reset();
}

// Renderiza el picker
void TilePicker::render() {
    if (!open_ || !frame_surface_) { return; }

    auto game_surface = Screen::get()->getRendererSurface();
    if (!game_surface) { return; }

    int frame_h = static_cast<int>(frame_dst_.h);

    if (frame_h <= visible_height_) {
        frame_surface_->render(nullptr, &frame_dst_);
    } else {
        int max_scroll = frame_h - visible_height_;
        scroll_y_ = std::clamp(scroll_y_, 0, max_scroll);

        SDL_FRect src = {.x = 0, .y = static_cast<float>(scroll_y_), .w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
        SDL_FRect dst = {.x = frame_dst_.x, .y = 0, .w = frame_dst_.w, .h = static_cast<float>(visible_height_)};
        frame_surface_->render(&src, &dst);
    }

    // Highlights (en game_surface, encima del frame)
    int out_cell = Tile::SIZE + spacing_out_;
    float tileset_screen_x = frame_dst_.x + BORDER_PAD;
    float tileset_screen_y = frame_dst_.y + BORDER_PAD - static_cast<float>(scroll_y_);
    constexpr auto TS = static_cast<float>(Tile::SIZE);

    // Si hay un drag rect activo, dibujar el rectángulo en progreso (blanco)
    // y omitir el highlight de hover individual.
    if (selecting_rect_ && rect_start_tile_ >= 0) {
        int end_tile = (hover_tile_ >= 0) ? hover_tile_ : last_valid_hover_;
        if (end_tile >= 0) {
            int c1 = rect_start_tile_ % tileset_width_;
            int r1 = rect_start_tile_ / tileset_width_;
            int c2 = end_tile % tileset_width_;
            int r2 = end_tile / tileset_width_;
            int col_min = std::min(c1, c2);
            int col_max = std::max(c1, c2);
            int row_min = std::min(r1, r2);
            int row_max = std::max(r1, r2);
            int cells_w = col_max - col_min + 1;
            int cells_h = row_max - row_min + 1;
            float rx = tileset_screen_x + static_cast<float>(col_min * out_cell);
            float ry = tileset_screen_y + static_cast<float>(row_min * out_cell);
            auto rw = static_cast<float>((cells_w * out_cell) - spacing_out_);
            auto rh = static_cast<float>((cells_h * out_cell) - spacing_out_);
            if (ry + rh > 0 && ry < static_cast<float>(visible_height_)) {
                SDL_FRect rect_box = {.x = rx, .y = ry, .w = rw, .h = rh};
                game_surface->drawRectBorder(&rect_box, 15);
            }
        }
    } else if (hover_tile_ >= 0) {
        // Highlight del tile bajo el cursor (blanco)
        int col = hover_tile_ % tileset_width_;
        int row = hover_tile_ / tileset_width_;
        float hx = tileset_screen_x + static_cast<float>(col * out_cell);
        float hy = tileset_screen_y + static_cast<float>(row * out_cell);
        if (hy >= 0 && hy + TS <= static_cast<float>(visible_height_)) {
            SDL_FRect highlight = {.x = hx, .y = hy, .w = TS, .h = TS};
            game_surface->drawRectBorder(&highlight, 15);
        }
    }

    // Highlight del tile actual (verde)
    if (current_tile_ >= 0) {
        int col = current_tile_ % tileset_width_;
        int row = current_tile_ / tileset_width_;
        float cx = tileset_screen_x + static_cast<float>(col * out_cell);
        float cy = tileset_screen_y + static_cast<float>(row * out_cell);
        if (cy >= 0 && cy + TS <= static_cast<float>(visible_height_)) {
            SDL_FRect cur_rect = {.x = cx, .y = cy, .w = TS, .h = TS};
            game_surface->drawRectBorder(&cur_rect, 9);
        }
    }
}

// Invoca el callback con el rect formado por rect_start_tile_ y el hover actual, luego cierra.
void TilePicker::commitRectSelection() {
    int end_tile = (hover_tile_ >= 0) ? hover_tile_ : last_valid_hover_;
    if (end_tile >= 0 && rect_start_tile_ >= 0) {
        int c1 = rect_start_tile_ % tileset_width_;
        int r1 = rect_start_tile_ / tileset_width_;
        int c2 = end_tile % tileset_width_;
        int r2 = end_tile / tileset_width_;
        int col_min = std::min(c1, c2);
        int col_max = std::max(c1, c2);
        int row_min = std::min(r1, r2);
        int row_max = std::max(r1, r2);
        int width = col_max - col_min + 1;
        int height = row_max - row_min + 1;
        if (on_select) { on_select(col_min, row_min, width, height); }
    }
    selecting_rect_ = false;
    rect_start_tile_ = -1;
    close();
}

// Maneja eventos del picker
void TilePicker::handleEvent(const SDL_Event& event) {
    if (!open_) { return; }

    if (event.type == SDL_EVENT_MOUSE_MOTION) {
        updateMousePosition();
    }

    if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
        if (event.button.button == SDL_BUTTON_LEFT) {
            if (allow_rect_ && hover_tile_ >= 0) {
                // Iniciar selección rectangular: el commit se hace al soltar
                selecting_rect_ = true;
                rect_start_tile_ = hover_tile_;
                last_valid_hover_ = hover_tile_;
            } else if (!allow_rect_ && hover_tile_ >= 0) {
                // Modo single-tile: confirmar inmediatamente
                int col = hover_tile_ % tileset_width_;
                int row = hover_tile_ / tileset_width_;
                if (on_select) { on_select(col, row, 1, 1); }
                close();
            }
        } else if (event.button.button == SDL_BUTTON_RIGHT) {
            close();
        }
    }

    if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT && selecting_rect_) {
        commitRectSelection();
    }

    if (event.type == SDL_EVENT_MOUSE_WHEEL) {
        scroll_y_ -= static_cast<int>(event.wheel.y) * Tile::SIZE * 2;
        int max_scroll = static_cast<int>(frame_dst_.h) - visible_height_;
        max_scroll = std::max(max_scroll, 0);
        scroll_y_ = std::clamp(scroll_y_, 0, max_scroll);
        updateMousePosition();
    }

    if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE) {
        close();
    }
}

// Calcula qué tile está bajo el cursor
void TilePicker::updateMousePosition() {
    float mouse_x = 0.0F;
    float mouse_y = 0.0F;
    SDL_GetMouseState(&mouse_x, &mouse_y);

    float render_x = 0.0F;
    float render_y = 0.0F;
    SDL_RenderCoordinatesFromWindow(Screen::get()->getRenderer(), mouse_x, mouse_y, &render_x, &render_y);

    SDL_FRect dst_rect = Screen::get()->getGameSurfaceDstRect();
    float game_x = render_x - dst_rect.x;
    float game_y = render_y - dst_rect.y;

    // Coordenada relativa al contenido del frame (con scroll)
    float rel_x = game_x - frame_dst_.x - BORDER_PAD;
    float rel_y = game_y - frame_dst_.y - BORDER_PAD + static_cast<float>(scroll_y_);

    // Convertir a tile teniendo en cuenta el spacing de salida
    int out_cell = Tile::SIZE + spacing_out_;
    int tile_x = static_cast<int>(rel_x) / out_cell;
    int tile_y = static_cast<int>(rel_y) / out_cell;

    // Verificar que estamos sobre un tile y no sobre el spacing
    int local_x = static_cast<int>(rel_x) % out_cell;
    int local_y = static_cast<int>(rel_y) % out_cell;
    bool on_tile = (local_x < Tile::SIZE && local_y < Tile::SIZE);

    if (on_tile && rel_x >= 0 && rel_y >= 0 &&
        tile_x >= 0 && tile_x < tileset_width_ &&
        tile_y >= 0 && tile_y < tileset_height_) {
        hover_tile_ = (tile_y * tileset_width_) + tile_x;
        last_valid_hover_ = hover_tile_;
    } else {
        hover_tile_ = -1;
    }
}

#endif  // _DEBUG
