// src/screens/portfolio/PortfolioHeatmap.cpp
#include "screens/portfolio/PortfolioHeatmap.h"

#include "screens/portfolio/PortfolioPanelHeader.h"
#include "ui/theme/Theme.h"

#include <QEvent>
#include <QGridLayout>
#include <QScrollArea>
#include <QVBoxLayout>

#include <algorithm>
#include <cmath>

namespace fincept::screens {

PortfolioHeatmap::PortfolioHeatmap(QWidget* parent) : QWidget(parent) {
    // Heatmap now shares the top 40% with chart + sector panel (commit #4),
    // not the full left column. Slightly wider band reads better at this size.
    setMinimumWidth(200);
    setMaximumWidth(240);
    build_ui();
}

void PortfolioHeatmap::build_ui() {
    setStyleSheet(
        QString("background:%1; border-right:1px solid %2;").arg(ui::colors::BG_SURFACE(), ui::colors::BORDER_DIM()));

    // Outer layout has zero margins so the unified header sits flush with the
    // panel edges; an inner content layout holds the body with its own padding.
    auto* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(0);

    // Unified panel header — HOLDINGS title + 3 mode buttons in the controls slot.
    auto header = make_panel_header(tr("HOLDINGS"), this);
    title_label_ = header.title_label;

    auto make_mode_btn = [&header](const QString& text) {
        auto* btn = new QPushButton(text);
        btn->setFixedSize(38, 20);
        btn->setCheckable(true);
        btn->setCursor(Qt::PointingHandCursor);
        // 10px / 700, square corners. Active = AMBER fill on BG_BASE text.
        btn->setStyleSheet(QString("QPushButton { background:transparent; color:%1; border:1px solid %2;"
                                   "  font-size:10px; font-weight:700; }"
                                   "QPushButton:checked { background:%3; color:%4; border-color:%3; }"
                                   "QPushButton:hover:!checked { color:%5; border-color:%5; }")
                               .arg(ui::colors::TEXT_TERTIARY(), ui::colors::BORDER_DIM(), ui::colors::AMBER(),
                                    ui::colors::BG_BASE(), ui::colors::TEXT_PRIMARY()));
        header.controls_slot->layout()->addWidget(btn);
        return btn;
    };

    pnl_btn_ = make_mode_btn(tr("PNL"));
    weight_btn_ = make_mode_btn(tr("WT"));
    day_btn_ = make_mode_btn(tr("DAY"));
    pnl_btn_->setChecked(true);

    // Two-letter labels are meaningless to a screen reader / new user.
    pnl_btn_->setAccessibleName(tr("Colour blocks by unrealized profit and loss"));
    pnl_btn_->setToolTip(pnl_btn_->accessibleName());
    weight_btn_->setAccessibleName(tr("Colour blocks by portfolio weight"));
    weight_btn_->setToolTip(weight_btn_->accessibleName());
    day_btn_->setAccessibleName(tr("Colour blocks by today's change"));
    day_btn_->setToolTip(day_btn_->accessibleName());

    auto set_mode = [this](portfolio::HeatmapMode m) {
        mode_ = m;
        pnl_btn_->setChecked(m == portfolio::HeatmapMode::Pnl);
        weight_btn_->setChecked(m == portfolio::HeatmapMode::Weight);
        day_btn_->setChecked(m == portfolio::HeatmapMode::DayChange);
        rebuild_blocks();
        emit mode_changed(m);
    };
    connect(pnl_btn_, &QPushButton::clicked, this, [=]() { set_mode(portfolio::HeatmapMode::Pnl); });
    connect(weight_btn_, &QPushButton::clicked, this, [=]() { set_mode(portfolio::HeatmapMode::Weight); });
    connect(day_btn_, &QPushButton::clicked, this, [=]() { set_mode(portfolio::HeatmapMode::DayChange); });

    layout->addWidget(header.header);

    // ── Content area below the header ────────────────────────────────────────
    auto* body = new QWidget(this);
    body->setStyleSheet("background:transparent;");
    auto* body_layout_outer = new QVBoxLayout(body);
    body_layout_outer->setContentsMargins(8, 8, 8, 6);
    body_layout_outer->setSpacing(5);
    layout->addWidget(body, 1);

    // Re-aim downstream code: the rest of build_ui() adds widgets to `layout`,
    // but we want them in `body`. Swap the variable name without a wide rewrite.
    layout = body_layout_outer;

    // Scrollable blocks area
    auto* scroll = new QScrollArea;
    scroll->setWidgetResizable(true);
    scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    scroll->setStyleSheet(QString("QScrollArea { border:none; background:transparent; }"
                                  "QScrollBar:vertical { width:4px; background:transparent; }"
                                  "QScrollBar::handle:vertical { background:%1; }")
                              .arg(ui::colors::BORDER_BRIGHT()));

    blocks_container_ = new QWidget(this);
    blocks_container_->setStyleSheet("background:transparent;");
    scroll->setWidget(blocks_container_);
    layout->addWidget(scroll, 1);

    // ── Top movers footer ────────────────────────────────────────────────────
    // The previous panel duplicated the StatsRibbon and the POSITIONS table:
    //   - 8-row selected-holding inspector → already in POSITIONS row data
    //   - RISK SCORE gauge                  → already in StatsRibbon RISK chip
    //   - HOLDINGS / CONC.TOP3 / VOL30      → already in StatsRibbon
    // All three were deleted. Top movers stays — it's not in the ribbon and
    // is contextual to the heatmap (the visible blocks).
    auto* movers_sep = new QWidget(this);
    movers_sep->setFixedHeight(1);
    movers_sep->setStyleSheet(QString("background:%1;").arg(ui::colors::BORDER_DIM()));
    layout->addWidget(movers_sep);

    movers_header_ = new QLabel(tr("TOP MOVERS"));
    movers_header_->setStyleSheet(QString("color:%1; font-size:10px; font-weight:700; letter-spacing:1px;"
                                          "  padding-top:4px;")
                                      .arg(ui::colors::TEXT_TERTIARY()));
    layout->addWidget(movers_header_);

    top_gainer_ = new QLabel;
    top_gainer_->setStyleSheet(QString("color:%1; font-size:11px; font-weight:600;").arg(ui::colors::POSITIVE()));
    layout->addWidget(top_gainer_);

    top_loser_ = new QLabel;
    top_loser_->setStyleSheet(QString("color:%1; font-size:11px; font-weight:600;").arg(ui::colors::NEGATIVE()));
    layout->addWidget(top_loser_);
}

void PortfolioHeatmap::set_holdings(const QVector<portfolio::HoldingWithQuote>& holdings) {
    holdings_ = holdings;
    rebuild_blocks();
    update_top_movers();
}

void PortfolioHeatmap::set_metrics(const portfolio::ComputedMetrics& metrics) {
    metrics_ = metrics;
    // Risk score / concentration / volatility are now in the StatsRibbon —
    // the heatmap no longer duplicates them. Keep the metrics_ field around
    // in case future heatmap modes want to colour blocks by risk score.
}

void PortfolioHeatmap::set_selected_symbol(const QString& symbol) {
    selected_symbol_ = symbol;
    rebuild_blocks();
}

void PortfolioHeatmap::set_currency(const QString& currency) {
    if (currency_ == currency)
        return;
    currency_ = currency;
    // Block tooltips carry the currency — refresh them when the selected
    // portfolio switches to a different denomination.
    rebuild_blocks();
}

QColor PortfolioHeatmap::block_color(const portfolio::HoldingWithQuote& h) const {
    double val = 0;
    switch (mode_) {
        case portfolio::HeatmapMode::Pnl:
            val = h.unrealized_pnl_percent;
            break;
        case portfolio::HeatmapMode::Weight:
            val = h.weight;
            break;
        case portfolio::HeatmapMode::DayChange:
            val = h.day_change_percent;
            break;
    }

    if (mode_ == portfolio::HeatmapMode::Weight) {
        // Amber: darker base, brighter at higher weights
        double t = std::min(val / 40.0, 1.0);
        int r = static_cast<int>(100 + t * 117); // 100 → 217
        int g = static_cast<int>(50 + t * 69);   // 50  → 119
        int b = static_cast<int>(6);
        return QColor(r, g, b);
    }

    // Green/Red: solid colors, brightness scales with magnitude
    double intensity = std::min(std::abs(val) / 20.0, 1.0); // saturates at ±20%
    if (val >= 0) {
        // Dark green → bright green
        int g = static_cast<int>(80 + intensity * 123); // 80 → 203
        int r = static_cast<int>(intensity * 22);
        return QColor(r, g, static_cast<int>(intensity * 30));
    } else {
        // Dark red → bright red
        int r = static_cast<int>(100 + intensity * 120); // 100 → 220
        return QColor(r, static_cast<int>(intensity * 20), static_cast<int>(intensity * 20));
    }
}

void PortfolioHeatmap::rebuild_blocks() {
    // Blocks are pooled: a refresh reuses the existing buttons and only touches
    // the ones whose rendered state actually changed. Each setStyleSheet is a
    // full CSS reparse + repolish, and this function runs on every 60 s poll,
    // every selection change and every mode toggle — allocating and restyling
    // one button per holding each time was the hot spot in this panel.
    if (!blocks_container_)
        return; // retranslate/theme events can arrive before build_ui() ran
    if (!blocks_grid_) {
        blocks_grid_ = new QGridLayout(blocks_container_);
        blocks_grid_->setContentsMargins(0, 0, 0, 0);
        blocks_grid_->setSpacing(2);
    }

    const int n = static_cast<int>(holdings_.size());

    for (int i = 0; i < n; ++i) {
        const auto& h = holdings_[i];
        const bool selected = (h.symbol == selected_symbol_);

        QPushButton* block = nullptr;
        if (i < blocks_.size()) {
            block = blocks_[i];
        } else {
            block = new QPushButton(blocks_container_);
            block->setCursor(Qt::PointingHandCursor);
            // The symbol lives on the widget so the (single) connection stays
            // valid when the block is recycled for a different holding.
            connect(block, &QPushButton::clicked, this, [this, block]() {
                const QString sym = block->property("symbol").toString();
                if (sym.isEmpty())
                    return;
                selected_symbol_ = sym;
                rebuild_blocks();
                emit symbol_selected(sym);
            });
            blocks_.append(block);
            blocks_grid_->addWidget(block, i / 2, i % 2);
        }

        block->setProperty("symbol", h.symbol);
        block->show();

        const int min_h = static_cast<int>(std::max(40.0, 40.0 + h.weight * 0.8));
        block->setFixedHeight(std::min(min_h, 70));

        const QColor bg = block_color(h);
        const QString border_style = selected ? QString("border:2px solid %1;").arg(ui::colors::AMBER())
                                              : QString("border:1px solid %1;").arg(ui::colors::BORDER_DIM());

        // Square corners (was border-radius:2px). Block label 11px (was 10px).
        const QString qss = QString("QPushButton { background:rgb(%1,%2,%3); %4"
                                    "  text-align:left; padding:4px 6px;"
                                    "  color:%6; font-size:11px; font-weight:700; }"
                                    "QPushButton:hover { border-color:%5; }")
                                .arg(bg.red())
                                .arg(bg.green())
                                .arg(bg.blue())
                                .arg(border_style, ui::colors::AMBER(), ui::colors::TEXT_PRIMARY());
        if (block->property("qss").toString() != qss) {
            block->setStyleSheet(qss);
            block->setProperty("qss", qss);
        }

        // Content
        const double chg_val = mode_ == portfolio::HeatmapMode::DayChange ? h.day_change_percent
                               : mode_ == portfolio::HeatmapMode::Pnl     ? h.unrealized_pnl_percent
                                                                         : h.weight;
        const QString chg_str =
            mode_ == portfolio::HeatmapMode::Weight
                ? QString("%1%").arg(QString::number(chg_val, 'f', 1))
                : QString("%1%2%").arg(chg_val >= 0 ? "+" : "").arg(QString::number(chg_val, 'f', 1));

        block->setText(QString("%1\n%2").arg(h.symbol, chg_str));
        // Market value is the one number the compact block cannot show —
        // surface it (and the portfolio currency) on hover.
        block->setToolTip(tr("%1 — %2 %3  •  %4% of portfolio")
                              .arg(h.symbol, currency_, QString::number(h.market_value, 'f', 2),
                                   QString::number(h.weight, 'f', 1)));
        block->setAccessibleName(h.symbol);
        block->setAccessibleDescription(block->toolTip());
    }

    // Surplus blocks from a previously larger portfolio: hide (a hidden widget
    // is skipped by QGridLayout so the rows collapse) and keep them pooled.
    for (int i = n; i < blocks_.size(); ++i)
        blocks_[i]->hide();

    // Push blocks up. Reset the previous stretch row first or a shrinking
    // portfolio leaves a stretched empty row mid-grid.
    const int last_row = n > 0 ? ((n - 1) / 2) : 0;
    if (stretch_row_ >= 0 && stretch_row_ != last_row + 1)
        blocks_grid_->setRowStretch(stretch_row_, 0);
    stretch_row_ = last_row + 1;
    blocks_grid_->setRowStretch(stretch_row_, 1);
}

void PortfolioHeatmap::update_top_movers() {
    if (holdings_.isEmpty()) {
        top_gainer_->setText("--");
        top_loser_->setText("--");
        return;
    }

    auto best = std::max_element(holdings_.begin(), holdings_.end(), [](const auto& a, const auto& b) {
        return a.day_change_percent < b.day_change_percent;
    });
    auto worst = std::min_element(holdings_.begin(), holdings_.end(), [](const auto& a, const auto& b) {
        return a.day_change_percent < b.day_change_percent;
    });

    top_gainer_->setText(QString("\u25B2 %1  %2%3%")
                             .arg(best->symbol)
                             .arg(best->day_change_percent >= 0 ? "+" : "")
                             .arg(QString::number(best->day_change_percent, 'f', 2)));

    top_loser_->setText(QString("\u25BC %1  %2%3%")
                            .arg(worst->symbol)
                            .arg(worst->day_change_percent >= 0 ? "+" : "")
                            .arg(QString::number(worst->day_change_percent, 'f', 2)));
}

void PortfolioHeatmap::changeEvent(QEvent* event) {
    if (event->type() == QEvent::LanguageChange)
        retranslateUi();
    QWidget::changeEvent(event);
}

void PortfolioHeatmap::retranslateUi() {
    if (title_label_)
        title_label_->setText(tr("HOLDINGS"));
    if (pnl_btn_)
        pnl_btn_->setText(tr("PNL"));
    if (weight_btn_)
        weight_btn_->setText(tr("WT"));
    if (day_btn_)
        day_btn_->setText(tr("DAY"));
    if (pnl_btn_) {
        pnl_btn_->setAccessibleName(tr("Colour blocks by unrealized profit and loss"));
        pnl_btn_->setToolTip(pnl_btn_->accessibleName());
    }
    if (weight_btn_) {
        weight_btn_->setAccessibleName(tr("Colour blocks by portfolio weight"));
        weight_btn_->setToolTip(weight_btn_->accessibleName());
    }
    if (day_btn_) {
        day_btn_->setAccessibleName(tr("Colour blocks by today's change"));
        day_btn_->setToolTip(day_btn_->accessibleName());
    }
    if (movers_header_)
        movers_header_->setText(tr("TOP MOVERS"));
    // Block labels/tooltips are formatted from data — re-render so the
    // localized tooltip template picks up the new locale.
    rebuild_blocks();
    // Block text is "SYMBOL\n+x.xx%" — pure data, no tr() needed.
    // top_gainer_/top_loser_ contents likewise are formatted from data; no
    // change on language switch.
}

void PortfolioHeatmap::refresh_theme() {
    setStyleSheet(
        QString("background:%1; border-right:1px solid %2;").arg(ui::colors::BG_SURFACE(), ui::colors::BORDER_DIM()));

    // Rebuild blocks picks up new theme colors for borders/text
    rebuild_blocks();
}

} // namespace fincept::screens
