#include "screens/dashboard/widgets/WatchlistWidget.h"

#include "datahub/DataHub.h"
#include "datahub/DataHubMetaTypes.h"
#include "ui/theme/Theme.h"

#include <QJsonArray>
#include <QLabel>

namespace fincept::screens::widgets {

WatchlistWidget::WatchlistWidget(QWidget* parent)
    : BaseWidget(tr("WATCHLIST"), parent, ui::colors::INFO),
      symbols_({"AAPL", "MSFT", "GOOGL", "AMZN", "NVDA", "TSLA", "META", "JPM"}) {

    auto* vl = content_layout();

    // Symbol input bar
    auto* input_row = new QWidget(this);
    auto* irl = new QHBoxLayout(input_row);
    irl->setContentsMargins(4, 4, 4, 4);
    irl->setSpacing(4);

    symbols_label_ = new QLabel(tr("SYMBOLS:"));
    irl->addWidget(symbols_label_);

    symbols_input_ = new QLineEdit(symbols_.join(", "));
    symbols_input_->setAccessibleName(tr("Watchlist symbols"));
    symbols_input_->setToolTip(tr("Comma-separated symbols, e.g. AAPL, MSFT, BTC-USD"));
    irl->addWidget(symbols_input_, 1);

    go_btn_ = new QPushButton(tr("GO"));
    go_btn_->setFixedWidth(32);
    go_btn_->setAccessibleName(tr("Apply watchlist symbols"));
    connect(go_btn_, &QPushButton::clicked, this, &WatchlistWidget::commit_symbols);
    // Enter in the symbol box is the same action as clicking GO.
    connect(symbols_input_, &QLineEdit::returnPressed, this, &WatchlistWidget::commit_symbols);
    irl->addWidget(go_btn_);

    vl->addWidget(input_row);

    // Table
    table_ = new ui::DataTable;
    table_->set_headers({tr("SYMBOL"), tr("PRICE"), tr("CHG"), tr("CHG%")});
    table_->set_column_widths({100, 90, 80, 70});
    vl->addWidget(table_);

    connect(this, &BaseWidget::refresh_requested, this, &WatchlistWidget::refresh_data);

    apply_styles();
    set_loading(true);
}

void WatchlistWidget::commit_symbols() {
    const QString text = symbols_input_->text().trimmed().toUpper();
    QStringList next;
    for (const QString& s : text.split(',')) {
        const QString trimmed = s.trimmed();
        if (!trimmed.isEmpty() && !next.contains(trimmed))
            next << trimmed;
    }
    if (next.isEmpty())
        return; // refuse to blank the watchlist — keep the previous set
    if (next == symbols_)
        return;
    symbols_ = next;
    // Dynamic symbol set: drop any cached rows for symbols no longer
    // tracked, then rewire subscriptions to the new set.
    row_cache_.clear();
    table_->clear_data();
    hub_resubscribe();
    // Persist through the canvas so the edited list survives a restart.
    emit config_changed(config());
}

QJsonObject WatchlistWidget::config() const {
    QJsonArray arr;
    for (const auto& s : symbols_)
        arr.append(s);
    QJsonObject o;
    o.insert("symbols", arr);
    return o;
}

void WatchlistWidget::apply_config(const QJsonObject& cfg) {
    QStringList next;
    const QJsonArray arr = cfg.value("symbols").toArray();
    for (const auto& v : arr) {
        const QString s = v.toString().trimmed().toUpper();
        if (!s.isEmpty() && !next.contains(s))
            next << s;
    }
    if (next.isEmpty() || next == symbols_)
        return;
    symbols_ = next;
    if (symbols_input_)
        symbols_input_->setText(symbols_.join(", "));
    row_cache_.clear();
    if (table_)
        table_->clear_data();
    if (isVisible())
        hub_resubscribe();
}

void WatchlistWidget::apply_styles() {
    symbols_label_->setStyleSheet(QString("color: %1; font-size: 9px; font-weight: bold; background: transparent;")
                                      .arg(ui::colors::TEXT_TERTIARY()));
    symbols_input_->setStyleSheet(
        QString("QLineEdit { background: %1; color: %2; border: 1px solid %3; "
                "font-size: 10px; padding: 2px 6px; font-family: Consolas; }"
                "QLineEdit:focus { border-color: %4; }")
            .arg(ui::colors::BG_BASE(), ui::colors::TEXT_PRIMARY(), ui::colors::BORDER_DIM(), ui::colors::AMBER()));
    go_btn_->setStyleSheet(QString("QPushButton { background: %1; color: %2; border: none; "
                                   "font-size: 9px; font-weight: bold; padding: 3px; }"
                                   "QPushButton:hover { background: %1; }")
                               .arg(ui::colors::AMBER(), ui::colors::BG_BASE()));
}

void WatchlistWidget::on_theme_changed() {
    apply_styles();
}

void WatchlistWidget::showEvent(QShowEvent* e) {
    BaseWidget::showEvent(e);
    if (!hub_active_)
        hub_resubscribe();
}

void WatchlistWidget::hideEvent(QHideEvent* e) {
    BaseWidget::hideEvent(e);
    if (hub_active_)
        hub_unsubscribe_all();
}

void WatchlistWidget::refresh_data() {
    if (symbols_.isEmpty())
        return;
    // User-triggered refresh: force-kick the hub for each current symbol.
    auto& hub = datahub::DataHub::instance();
    QStringList topics;
    topics.reserve(symbols_.size());
    for (const auto& sym : symbols_)
        topics.append(QStringLiteral("market:quote:") + sym);
    hub.request(topics, /*force=*/true); // user-triggered: bypass min_interval
}

void WatchlistWidget::hub_resubscribe() {
    auto& hub = datahub::DataHub::instance();
    // Drop old subscriptions wholesale — symbol set may have changed.
    hub.unsubscribe(this);
    // Kick off the determinate loading overlay — count is the live symbol
    // count, not a hardcoded constant, so adding/removing symbols via the
    // GO button updates the denominator correctly.
    set_loading_progress(row_cache_.size(), symbols_.size());
    for (const auto& sym : symbols_) {
        const QString topic = QStringLiteral("market:quote:") + sym;
        hub.subscribe(this, topic, [this, sym](const QVariant& v) {
            if (!v.canConvert<services::QuoteData>())
                return;
            row_cache_.insert(sym, v.value<services::QuoteData>());
            // Numerator = unique symbols delivered so far; the overlay
            // animates the count up and fades out when row_cache_ is full.
            set_loading_progress(row_cache_.size(), symbols_.size());
            // One render per delivery burst, not one per symbol.
            schedule_render([this]() { render_from_cache(); });
        });
    }
    hub_active_ = true;
}

void WatchlistWidget::hub_unsubscribe_all() {
    datahub::DataHub::instance().unsubscribe(this);
    hub_active_ = false;
}

void WatchlistWidget::render_from_cache() {
    table_->clear_data();
    for (const auto& sym : symbols_) {
        auto it = row_cache_.constFind(sym);
        if (it == row_cache_.constEnd())
            continue;
        const auto& q = it.value();
        table_->add_row({q.symbol, QString("$%1").arg(q.price, 0, 'f', 2),
                         QString("%1%2").arg(q.change >= 0 ? "+" : "").arg(q.change, 0, 'f', 2),
                         QString("%1%2%").arg(q.change_pct >= 0 ? "+" : "").arg(q.change_pct, 0, 'f', 2)});
        int row = table_->rowCount() - 1;
        table_->set_cell_color(row, 2, ui::change_color(q.change_pct));
        table_->set_cell_color(row, 3, ui::change_color(q.change_pct));
    }
}

void WatchlistWidget::retranslateUi() {
    BaseWidget::retranslateUi();
    set_title(tr("WATCHLIST"));
    if (symbols_label_)
        symbols_label_->setText(tr("SYMBOLS:"));
    if (go_btn_)
        go_btn_->setText(tr("GO"));
    if (table_)
        table_->set_headers({tr("SYMBOL"), tr("PRICE"), tr("CHG"), tr("CHG%")});
}

} // namespace fincept::screens::widgets
