// compute_column_stats — estadisticas por columna (mean, p25/p50/p75, hist, top).
// Promovido del playground tables (issue 0081-F). Pura — sin I/O ni estado.

#include "core/compute_column_stats.h"
#include "core/auto_detect_type.h"  // parse_number

#include <algorithm>
#include <cmath>
#include <unordered_map>
#include <vector>

namespace data_table {

ColStats compute_column_stats(const char* const* cells, int rows, int cols,
                              int col, int unique_cap,
                              const int* indices, int n_indices)
{
    ColStats s;
    if (col < 0 || col >= cols) return s;
    bool use_idx = (indices != nullptr && n_indices > 0);
    int  n       = use_idx ? n_indices : rows;
    s.total = n;
    std::unordered_map<std::string, int> counts;
    if (unique_cap > 0) counts.reserve(std::min(unique_cap, n));
    bool all_numeric = true;
    std::vector<double> nums;
    nums.reserve(n);
    for (int i = 0; i < n; ++i) {
        int r = use_idx ? indices[i] : i;
        if (r < 0 || r >= rows) continue;
        const char* c = cells[r * cols + col];
        if (!c || !*c) { s.empty_count++; continue; }
        double v;
        if (parse_number(c, v)) {
            if (s.numeric_count == 0) { s.min = v; s.max = v; }
            else {
                if (v < s.min) s.min = v;
                if (v > s.max) s.max = v;
            }
            s.sum += v;
            s.numeric_count++;
            nums.push_back(v);
        } else {
            all_numeric = false;
        }
        if (unique_cap == 0 || (int)counts.size() < unique_cap) {
            counts[c]++;
        } else {
            auto it = counts.find(c);
            if (it != counts.end()) it->second++;
            else                    s.unique_capped = true;
        }
    }
    s.unique_count = (int)counts.size();
    s.numeric = all_numeric && s.numeric_count > 0;
    if (s.numeric_count > 0) s.mean = s.sum / s.numeric_count;
    // Top 8 categorias por count descendente.
    if (!counts.empty()) {
        std::vector<std::pair<std::string,int>> v(counts.begin(), counts.end());
        int topN = std::min<int>(8, (int)v.size());
        std::partial_sort(v.begin(), v.begin() + topN, v.end(),
            [](const auto& a, const auto& b){ return a.second > b.second; });
        v.resize(topN);
        s.top_categories = std::move(v);
    }
    if (s.numeric && !nums.empty()) {
        std::sort(nums.begin(), nums.end());
        auto pct = [&](double p) {
            double idx = p * (nums.size() - 1);
            size_t lo = (size_t)idx;
            size_t hi = std::min(lo + 1, nums.size() - 1);
            double t  = idx - lo;
            return nums[lo] * (1.0 - t) + nums[hi] * t;
        };
        s.p25 = pct(0.25);
        s.p50 = pct(0.50);
        s.p75 = pct(0.75);

        s.hist.assign(HIST_BINS, 0.0f);
        double range = s.max - s.min;
        if (range <= 0) {
            s.hist[HIST_BINS / 2] = (float)nums.size();
        } else {
            for (double val : nums) {
                int b = (int)((val - s.min) / range * HIST_BINS);
                if (b < 0) b = 0;
                if (b >= HIST_BINS) b = HIST_BINS - 1;
                s.hist[b] += 1.0f;
            }
        }
    }
    return s;
}

} // namespace data_table
