// data_table_types — types compartidos del stack TQL (Table Query Language).
// Promovido al registry desde cpp/apps/primitives_gallery/playground/tables/.
// Ver issue 0081 + docs/TQL.md. Pure value types + enums.
// Issue 0081-N: CellRenderer / ColumnSpec / BadgeRule / IconMapEntry (v1.1.0).
// v1.4.0: ChipRule / ColorStop / CategoricalChip / ColorScale renderers.
#pragma once

#include "compute_column_stats.h"

#include <cstddef>
#include <cstdint>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>

namespace data_table {

// ----------------------------------------------------------------------------
// Operadores de filtro.
// ----------------------------------------------------------------------------
enum class Op {
    Eq, Neq, Gt, Gte, Lt, Lte,
    Contains, NotContains, StartsWith, EndsWith
};

// ----------------------------------------------------------------------------
// Tipo de columna. Declarado por caller o auto-detectado.
// ----------------------------------------------------------------------------
enum class ColumnType {
    Auto, String, Int, Float, Bool, Date, Json
};

// ----------------------------------------------------------------------------
// Derived column: inmutable. Dos modos:
//   1) Retipo puro: source_col >= 0, formula == "". Cells del origen.
//   2) Formula:     source_col == -1, formula no vacia. Eval por Lua.
// ----------------------------------------------------------------------------
struct DerivedColumn {
    int         source_col = -1;
    ColumnType  type       = ColumnType::String;
    std::string name;
    std::string formula;        // "" = retipado puro; resto = body Lua
    int         lua_id  = -1;   // referencia en lua_engine; -1 si no compilado
    std::string compile_error;
};

// ----------------------------------------------------------------------------
// Filtro: col index en eff_headers + op + value.
// ----------------------------------------------------------------------------
struct Filter {
    int         col;
    Op          op;
    std::string value;
};

// ColorStop: one stop in an N-color gradient. Used by ColorRule (NumericRange)
// y por ColumnSpec (ColorScale renderer). Definido aqui (no abajo) para que
// ColorRule pueda contenerlo sin forward decl.
struct ColorStop {
    float       position;   // 0.0 (leftmost/min) to 1.0 (rightmost/max)
    std::string color;      // "#rrggbb" hex color at this stop
};

// ----------------------------------------------------------------------------
// Drill extendido (fase 10). Ver issue 0079.
// Definidos aqui (no al final) para que State pueda contener vector<DrillStep>.
// ----------------------------------------------------------------------------
enum class DateGranularity { None, Year, Month, Week, Day, Hour };

enum class FilterPreset { Last7d, Last30d, Last90d, ExcludeNulls, NonZero };

// Step de drill grabado para history undo/redo (fase 10).
struct DrillStep {
    int     target_stage      = -1;   // stage donde se anadio el filter
    int     filter_pos        = -1;   // index en target_stage.filters
    int     prev_active_stage = 0;    // active_stage antes del drill
    Filter  added;                    // filter para redo
};

// ----------------------------------------------------------------------------
// ColorRule kind (v1.5.0): pintado condicional con tres modos.
//   CellBg          — bg de celda cuando valor == `equals` (back-compat).
//   CategoricalDot  — dot a la izquierda del texto, colores autoasignados por
//                     valor distinto (palette deterministica). Util para
//                     categoricas con muchos valores donde no quieres definir
//                     cada color a mano.
//   NumericRange    — gradiente continuo de N colores (range_stops) sobre el
//                     bg de la celda. Para columnas numericas.
// ----------------------------------------------------------------------------
enum class ColorRuleKind : uint8_t {
    CellBg         = 0,   // legacy (equals + color)
    CategoricalDot = 1,   // dots autoasignados por valor distinto
    NumericRange   = 2,   // gradiente N-color sobre rango numerico
};

// ColorRule: pintado condicional de celdas (UI helper).
// v1.5.0: anade `kind` + campos para CategoricalDot / NumericRange.
struct ColorRule {
    int            col;
    std::string    equals;                  // CellBg: match value; otros: ignorado
    unsigned int   color;                   // CellBg: bg color; otros: ignorado

    // v1.5.0 fields (defaults preservan back-compat — kind=CellBg).
    ColorRuleKind  kind = ColorRuleKind::CellBg;

    // CategoricalDot: alpha del relleno del dot (0..1). Tamaño en px (default 6).
    float          dot_alpha = 1.0f;
    float          dot_radius_px = 4.0f;
    // Si vacio -> autoasigna desde palette interna. Si no, mapping fijo:
    //   pares (valor, "#rrggbb") consultados en orden.
    std::vector<std::pair<std::string, std::string>> dot_map;

    // NumericRange: gradiente continuo. range_min..range_max + N>=2 stops.
    double         range_min   = 0.0;
    double         range_max   = 1.0;
    float          range_alpha = 0.25f;     // [0..1]; bg tint opacity
    std::vector<ColorStop> range_stops;     // declarado mas abajo; vacio -> default green→amber→red
};

// ----------------------------------------------------------------------------
// Aggregations (TQL stages 1+).
// ----------------------------------------------------------------------------
enum class AggFn {
    Count, Sum, Avg, Min, Max, Distinct, Stddev,
    Median, P25, P75, P90, P99, Percentile
};

struct Aggregation {
    AggFn       fn = AggFn::Count;
    std::string col;         // ignorado para Count
    double      arg = 0.0;   // para Percentile (0..1)
    std::string alias;       // vacio -> auto-generado via aggregation_alias()
};

struct SortClause {
    std::string col;
    bool        desc = false;
};

// Stage: layer de TQL. Stage 0 = Raw (sin breakouts/aggregations).
// Stage 1+ pueden agrupar. Cada stage consume output del anterior.
struct Stage {
    std::vector<Filter>          filters;
    std::vector<DerivedColumn>   derived;       // expressions de este stage
    std::vector<std::string>     breakouts;     // col names del INPUT de este stage
    std::vector<Aggregation>     aggregations;
    std::vector<SortClause>      sorts;
};

// Output de compute_stage. Posee `cell_backing` (strings nuevos para
// resultados agregados) y `cells` (punteros row-major a backing o a
// `in_cells` original para passthrough).
struct StageOutput {
    std::vector<std::string>  cell_backing;
    std::vector<const char*>  cells;
    int                       rows = 0;
    int                       cols = 0;
    std::vector<std::string>  headers;
    std::vector<ColumnType>   types;
};

// ----------------------------------------------------------------------------
// ViewMode: tipo de visualizacion a renderizar sobre el output del stage activo.
// ----------------------------------------------------------------------------
enum class ViewMode {
    Table,
    // Bars
    Bar, Column, GroupedBar, StackedBar,
    // Lines / area
    Line, Area, Stairs,
    // Points
    Scatter, Bubble,
    // Distribution
    Histogram, Histogram2D, Heatmap, BoxPlot,
    // Stems / signals
    Stem, ErrorBars,
    // Composition
    Pie, Donut, Funnel, Waterfall,
    // Single values
    KPI, KPIGrid,
    // Specialized
    Candlestick, Radar,
};

// ----------------------------------------------------------------------------
// Joins (MBQL-style). Ver issue 0078.
// ----------------------------------------------------------------------------
enum class JoinStrategy { Left, Inner, Right, Full };

// ----------------------------------------------------------------------------
// CellRenderer: declarative rendering mode per column (issue 0081-N, v1.1.0).
// Phase 2 (issue 0081-O, v1.2.0): Button=5 added.
// Phase 2.5 (issue 0081-O.5, v1.3.0): Dots=8 added (inline status timeline).
// v1.4.0: CategoricalChip=9, ColorScale=10.
// ----------------------------------------------------------------------------
enum class CellRenderer : uint8_t {
    Text            = 0,   // default — current behavior
    Badge           = 1,   // colored badge per-value
    Progress        = 2,   // progress bar (0..1 or 0..100)
    Duration        = 3,   // milliseconds with color gradient
    Icon            = 4,   // icon lookup by value string
    Button          = 5,   // clickable button; emits TableEvent::ButtonClick
    // 6, 7: reserved for Phase 3 (TextInput, Custom).
    Dots            = 8,   // inline dots sparkline; cell = separator-delimited tokens
    CategoricalChip = 9,   // filled circle (8px) to left of text; color by value match
    ColorScale      = 10,  // continuous N-color gradient tint on cell background
};

// ----------------------------------------------------------------------------
// TableEventKind: kinds of events emitted by render() via events_out.
// Issue 0081-O, v1.2.0.
// ----------------------------------------------------------------------------
enum class TableEventKind : uint8_t {
    ButtonClick    = 1,  // CellRenderer::Button was clicked
    RowDoubleClick = 2,  // row was double-clicked (left button)
    RowRightClick  = 3,  // row right-clicked (open context menu)
    CellEdit       = 4,  // reserved for Phase 3 (TextInput)
};

// TableEvent: carries the context of a single UI interaction.
struct TableEvent {
    TableEventKind kind;
    int            row       = -1;   // index in TableInput (not StageOutput)
    int            col       = -1;   // column index in TableInput
    std::string    column_id;        // ColumnSpec.id of the column
    std::string    action_id;        // for ButtonClick: ColumnSpec.button_action
    std::string    value;            // cell value at the click point
};

// BadgeRule: maps a cell value to a colored badge label.
struct BadgeRule {
    std::string value;      // exact match (case-sensitive)
    std::string color_hex;  // "#rrggbb" or "rrggbb"
    std::string label;      // optional visual override; "" -> use value as-is
};

// IconMapEntry: maps a cell value to a Tabler icon macro name + optional color.
struct IconMapEntry {
    std::string value;
    std::string icon_name;  // e.g. "TI_CHECK", "TI_X" — resolved via static map
    std::string color_hex;  // optional; "" -> default text color
};

// ChipRule: maps a cell value to a dot color for CategoricalChip renderer (v1.4.0).
// If no rule matches, no dot is drawn (fallback: plain text only).
struct ChipRule {
    std::string match;      // exact match (case-sensitive) against cell value
    std::string color;      // "#rrggbb" hex color for the filled circle
};

// (ColorStop defined earlier, near Filter, so ColorRule can reference it.)

// ColumnSpec: rendering spec for one column. Indexed by column position.
struct ColumnSpec {
    std::string  id;                                    // stable id, used in TQL
    CellRenderer renderer = CellRenderer::Text;

    // Badge
    std::vector<BadgeRule>    badges;

    // Progress: cell value is float 0..1 (or 0..100 if progress_scale_100 = true)
    bool                       progress_scale_100 = false;
    std::string                progress_color_hex;      // override bar color; "" -> ImPlot auto

    // Duration: cell value in milliseconds (float); gradient green<warn<yellow<error<red
    float                      duration_warn_ms  = 1000.0f;
    float                      duration_error_ms = 5000.0f;

    // Icon
    std::vector<IconMapEntry>  icon_map;

    // Button (Phase 2, v1.2.0): CellRenderer::Button
    std::string                button_action;      // semantic id the app processes
    std::string                button_label;       // button text; "" -> use cell value
    std::string                button_color_hex;   // optional button color; "" -> default

    // Tooltip (Phase 2, v1.2.0): per-cell hover tooltip
    std::string                tooltip;            // text; "auto" -> show cell value
    bool                       tooltip_on_hover = false;  // if true, show on hover

    // Dots (Phase 2.5, v1.3.0): CellRenderer::Dots — inline status sparkline.
    // Cell value is a separator-delimited string of tokens, e.g. "ok,error,ok".
    // Each token is looked up in `badges` for color. Dots are drawn as filled circles
    // via ImDrawList (font-independent — no Unicode glyph dependency).
    // Note: BadgeRule.label is ignored for Dots; it only applies to the Badge renderer.
    char                       dots_separator  = ',';    // separator between tokens
    float                      dots_glyph_size = 0.0f;  // glyph size px; 0 = default font size
    int                        dots_max        = 0;      // hard limit on dots shown; 0 = no limit
    bool                       dots_show_count = false;  // if true, appends " (N)" after dots

    // CategoricalChip (v1.4.0): CellRenderer::CategoricalChip.
    // Draws a filled circle (radius ~4px) to the left of the cell text.
    // Color is determined by matching cell value against `chips` rules.
    // Always visible (not hover-only). If no rule matches, no dot is drawn.
    std::vector<ChipRule>      chips;                   // value → color rules

    // ColorScale (v1.4.0): CellRenderer::ColorScale.
    // Maps numeric cell value to a background tint via N-color gradient LERP.
    // Low alpha so text remains legible.
    double                     range_min   = 0.0;       // value at t=0.0
    double                     range_max   = 1.0;       // value at t=1.0
    float                      range_alpha = 0.25f;     // [0..1]; background tint opacity
    std::vector<ColorStop>     range_stops;             // N≥2 stops; empty → default green→amber→red
};

// ----------------------------------------------------------------------------
// Tabla extra pasada al render() para joins. Owner externo (caller).
// ----------------------------------------------------------------------------
struct TableInput {
    std::string                 name;       // identificador estable (matchea Join.source)
    std::vector<std::string>    headers;
    std::vector<ColumnType>     types;
    const char* const*          cells = nullptr;  // row-major, headers.size() cols x rows filas
    int                         rows  = 0;
    int                         cols  = 0;

    // NEW (issue 0081-N, v1.1.0): optional declarative renderers per column.
    // empty -> all columns use default Text rendering (back-compat preserved).
    // If non-empty, size must equal cols (or be 0 for "no specs").
    // column_specs are caller-managed; not persisted in TQL yet (Phase 2).
    std::vector<ColumnSpec>     column_specs;
};

// Join clause: une la tabla actual con `source` por las parejas `on`,
// prefijando las cols del derecho con `alias.`.
struct Join {
    std::string                                          alias;
    std::string                                          source;
    std::vector<std::pair<std::string, std::string>>     on;        // {left_col, right_col}
    JoinStrategy                                         strategy = JoinStrategy::Left;
    std::vector<std::string>                             fields;    // vacio = all del derecho
};

// ----------------------------------------------------------------------------
// ViewConfig: overrides manuales de auto-detect para la vista activa.
// ----------------------------------------------------------------------------
struct ViewConfig {
    std::string                 x_col;        // single: scatter, line, hist2d
    std::vector<std::string>    y_cols;       // 1..N: line/area/bar/etc
    std::string                 size_col;     // bubble
    std::string                 cat_col;      // bar/pie/funnel/box override
    unsigned int                primary_color = 0;     // 0 = ImPlot auto
    int                         hist_bins     = 0;     // 0 = Sturges
    float                       pie_radius    = 0.0f;  // 0 = default
    bool                        show_legend   = true;
    bool                        show_markers  = false; // line/area markers
    bool                        locked        = false; // disable pan/zoom
    mutable bool                fit_request   = false; // consumed by viz::render
};

// VizPanel: viz adicional sobre el mismo StageOutput.
struct VizPanel {
    ViewMode    display = ViewMode::Bar;
    ViewConfig  config;
    mutable ViewMode last_non_table = ViewMode::Bar;
};

// ----------------------------------------------------------------------------
// StringPool — interning de strings para columnas de texto (issue 0133).
// Una instancia por State (NOT global) para aislar tablas independientes.
//
// intern(sv) devuelve un indice uint32_t estable para la vida del rebuild.
// El pool se limpia (clear()) al inicio de cada rebuild de snapshot columnar.
//
// Invariante de invalidacion de string_view:
//   - El vector `strings` se reserva con reserve() ANTES del primer intern()
//     para evitar reallocs que invalidarian los string_view del mapa.
//     Si la estimacion es insuficiente (columna con mas unicos de lo esperado),
//     el mapa se reconstruye post-push_back: intern() verifica cap antes de
//     insertar en el map para cubrir este caso.
// ----------------------------------------------------------------------------
struct StringPool {
    std::vector<std::string>                   strings;  // strings unicos, por indice
    std::unordered_map<std::string_view, uint32_t> index; // sv→id (sv apunta a strings[i])

    void clear() {
        strings.clear();
        index.clear();
    }

    // intern: inserta si no existe. Devuelve indice estable.
    // INVARIANTE: reserve() ANTES del primer intern() por columna para evitar
    // reallocs que invalidarian los string_view del mapa. Si la estimacion fue
    // insuficiente, forzamos reserve(size+1) ANTES de emplace_back para que
    // la realloc ocurra antes de que cualquier sv del mapa apunte al buffer
    // viejo — y reconstruimos el mapa desde cero tras la realloc.
    uint32_t intern(std::string_view sv) {
        auto it = index.find(sv);
        if (it != index.end()) return it->second;
        uint32_t id = (uint32_t)strings.size();
        if (strings.size() == strings.capacity()) {
            // Realloc inminente: hacerlo ANTES de insertar en index para que
            // los string_view existentes no queden dangling. Tras el reserve,
            // reconstruimos el index desde cero porque los punteros cambiaron.
            strings.reserve(strings.capacity() == 0 ? 64 : strings.capacity() * 2);
            index.clear();
            for (uint32_t i = 0; i < (uint32_t)strings.size(); ++i)
                index.emplace(std::string_view(strings[i]), i);
        }
        strings.emplace_back(sv);
        // string_view apunta al almacenamiento interno (strings[id]), estable
        // porque acabamos de garantizar capacidad suficiente.
        index.emplace(std::string_view(strings[id]), id);
        return id;
    }

    const std::string& at(uint32_t id) const { return strings[id]; }
    bool empty() const { return strings.empty(); }
};

// ----------------------------------------------------------------------------
// State: stage pipeline + viz globales.
// ----------------------------------------------------------------------------
struct State {
    std::vector<Stage>          stages;
    int                         active_stage = 0;
    ViewMode                    display = ViewMode::Table;
    ViewConfig                  viz_config;
    std::vector<VizPanel>       extra_panels;
    std::vector<Join>           joins;     // aplicado antes de stages[0]
    std::string                 main_source;  // name de TableInput; vacio -> tables[0]

    std::vector<ColorRule>      color_rules;
    std::vector<bool>           col_visible;
    std::vector<int>            col_order;

    // aux_column_specs (Phase 2, v1.2.0): TQL-persisted column specs sidecar.
    // Parallel to tables[]; aux_column_specs[0] corresponds to tables[0], etc.
    // Empty = no TQL-persisted specs.  When non-empty, render() merges these
    // into TableInput.column_specs if the caller passed an empty column_specs.
    // Caller-provided column_specs take precedence over aux_column_specs.
    std::vector<std::vector<ColumnSpec>> aux_column_specs;

    // Per-table "Show UI" toggle. Moved from global UiCache to per-State so each
    // table's chrome (chips bar) can be toggled independently (issue: multiple
    // tables on screen, "Show UI" used to flip all at once).
    // Defaults: user_set=true + visible=false => chrome closed by default, ignoring
    // the API arg show_chrome from frame 1 (preserves legacy behavior).
    bool                        chrome_user_set     = true;
    bool                        chrome_user_visible = false;

    // ------------------------------------------------------------------------
    // v1.5.0 — per-table UI state. Antes vivia en UiState singleton, lo cual
    // hacia que toggle "Show stats" / seleccion de celdas / drill-history /
    // row inspector se aplicasen a TODAS las tablas a la vez. Ahora cada
    // State los lleva. Modal/popup state (ask AI, edit chips, etc.) sigue en
    // UiState porque solo uno esta abierto en toda la app a la vez.
    // ------------------------------------------------------------------------

    // Stats panel (cabecera de col muestra min/max/percentiles/hist).
    bool                     stats_mode         = false;
    std::vector<ColStats>    stats_cache;
    // Invalidacion del cache (data-identity y filter-set).
    const char* const*       stats_last_cells    = nullptr;
    int                      stats_last_rows     = -1;
    int                      stats_last_eff_cols = -1;
    size_t                   stats_last_filter_h = (size_t)-1;
    int                      stats_last_visible  = -1;

    // Cell selection (drag-select rectangular Ctrl+C-able).
    int  sel_anchor_row = -1;
    int  sel_anchor_col = -1;
    int  sel_end_row    = -1;
    int  sel_end_col    = -1;
    bool sel_active     = false;
    bool sel_dragging   = false;

    // Row inspector modal target (-1 = closed).
    int  inspect_row     = -1;
    bool inspect_open    = false;

    // Drill history (fase 10) — per-table porque cada tabla tiene su propio
    // pipeline de filters.
    std::vector<DrillStep> drill_back;
    std::vector<DrillStep> drill_forward;

    // String interning pool (issue 0133, Change 2).
    // Limpiado y repoblado en cada rebuild del snapshot columnar.
    // NOT global — una instancia por State para aislar tablas independientes.
    StringPool string_pool;

    // Helpers (definidos en compute_stage.cpp).
    Stage&       raw();
    const Stage& raw() const;
    Stage&       active();
    const Stage& active_const() const;
    void         ensure_stage0();
};

} // namespace data_table
