#include "DeadCodeDialog.h"

#include <QtConcurrent/QtConcurrent>
#include <QFileInfo>
#include <QDir>
#include <QFont>
#include <QSettings>
#include <QCloseEvent>
#include <QFileDialog>
#include <QTextStream>
#include <QMessageBox>
#include <QHeaderView>
#include <QDateTime>
#include <QMetaObject>

DeadCodeDialog::DeadCodeDialog(QWidget *parent)
    : QDialog(parent, Qt::Window)
{
    setWindowTitle(tr("Tote Funktionen – BareCode"));
    setMinimumSize(650, 500);

    QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
    if (s.contains("deadcode/geometry"))
    {
        restoreGeometry(s.value("deadcode/geometry").toByteArray());
    }
    else
    {
        resize(780, 620);
    }

    m_analyzer = new DeadCodeAnalyzer(this);
    m_watcher  = new QFutureWatcher<QList<DeadCodeAnalyzer::DeadFunction>>(this);

    connect(m_watcher, &QFutureWatcher<QList<DeadCodeAnalyzer::DeadFunction>>::finished,
            this, &DeadCodeDialog::onAnalysisFinished);

    // Fortschritts-Signal aus dem Analyzer-Thread sicher in den UI-Thread leiten
    connect(m_analyzer, &DeadCodeAnalyzer::progressUpdate,
            this, &DeadCodeDialog::onProgressUpdate,
            Qt::QueuedConnection);

    setupUi();
}

// ---------------------------------------------------------------------------
// UI
// ---------------------------------------------------------------------------
void DeadCodeDialog::setupUi()
{
    QVBoxLayout *root = new QVBoxLayout(this);
    root->setSpacing(8);
    root->setContentsMargins(12, 12, 12, 12);

    // ---- Warnbox ----
    QFrame *warningBox = new QFrame(this);
    warningBox->setStyleSheet(
        "QFrame {"
        "  background: #3a2a00;"
        "  border: 1px solid #7a5a00;"
        "  border-radius: 4px;"
        "}"
    );
    QHBoxLayout *warnLayout = new QHBoxLayout(warningBox);
    warnLayout->setContentsMargins(8, 6, 8, 6);

    QLabel *warnIcon = new QLabel(warningBox);
    warnIcon->setFixedSize(20, 20);
    warnIcon->setStyleSheet(
        "background: #ffcc00;"
        "color: #1a1a00;"
        "font-weight: bold;"
        "font-size: 13px;"
        "border-radius: 10px;"
    );
    warnIcon->setText("!");
    warnIcon->setAlignment(Qt::AlignCenter);
    warnLayout->addWidget(warnIcon);

    QLabel *warnText = new QLabel(
        tr("Kandidatenliste — kein Aufruf per Regex gefunden. "
           "Dynamische Aufrufe (call_user_func, Strings, Hooks) werden nicht erkannt. "
           "Bitte vor dem Löschen manuell prüfen."),
        warningBox
    );
    warnText->setWordWrap(true);
    warnText->setStyleSheet("color: #ffcc88;");
    warnLayout->addWidget(warnText, 1);
    root->addWidget(warningBox);

    // ---- Exclude-Verzeichnisse ----
    QHBoxLayout *excludeRow = new QHBoxLayout();
    excludeRow->addWidget(new QLabel(tr("Ausschließen:"), this));

    m_excludeEdit = new QLineEdit(this);
    m_excludeEdit->setPlaceholderText(tr("vendor lib node_modules  (leerzeichen-getrennt)"));
    m_excludeEdit->setToolTip(tr(
        "Verzeichnisnamen die bei der Analyse übersprungen werden.\n"
        "Leerzeichen-getrennt, z.B.: vendor lib node_modules cache"
    ));

    // Letzte Einstellung wiederherstellen
    QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
    m_excludeEdit->setText(s.value("deadcode/excludeDirs", "vendor lib node_modules").toString());

    excludeRow->addWidget(m_excludeEdit, 1);

    // Verzeichnis-Auswahl Button
    QPushButton *btnBrowse = new QPushButton(tr("+ Verzeichnis"), this);
    btnBrowse->setToolTip(tr("Verzeichnis aus dem Projekt auswählen und zur Ausschlussliste hinzufügen"));
    connect(btnBrowse, &QPushButton::clicked, this, [this]()
    {
        const QString startPath = m_projectRoot.isEmpty()
                                  ? QDir::homePath()
                                  : m_projectRoot;

        const QString dir = QFileDialog::getExistingDirectory(
            this,
            tr("Verzeichnis ausschließen"),
            startPath,
            QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks
        );

        if (dir.isEmpty())
        {
            return;
        }

        // Nur den letzten Verzeichnisnamen verwenden, nicht den vollen Pfad
        // So funktioniert der Exclude auch für gleichnamige Unterverzeichnisse
        const QString dirName = QFileInfo(dir).fileName();

        // Prüfen ob bereits in der Liste
        const QStringList current = m_excludeEdit->text()
                                        .simplified()
                                        .split(' ', Qt::SkipEmptyParts);
        if (current.contains(dirName, Qt::CaseInsensitive))
        {
            return;
        }

        // Anhängen
        QString newText = m_excludeEdit->text().trimmed();
        if (!newText.isEmpty())
        {
            newText += ' ';
        }
        newText += dirName;
        m_excludeEdit->setText(newText);
    });
    excludeRow->addWidget(btnBrowse);

    // Löschen-Button — ganzes Feld leeren
    QPushButton *btnClear = new QPushButton(tr("✕"), this);
    btnClear->setFixedWidth(28);
    btnClear->setFlat(true);
    btnClear->setToolTip(tr("Ausschlussliste leeren"));
    connect(btnClear, &QPushButton::clicked, this, [this]()
    {
        m_excludeEdit->clear();
    });
    excludeRow->addWidget(btnClear);

    root->addLayout(excludeRow);

    // ---- Steuerleiste ----
    QHBoxLayout *ctrlRow = new QHBoxLayout();

    m_btnAnalyze = new QPushButton(tr("Analyse starten"), this);
    ctrlRow->addWidget(m_btnAnalyze);

    m_btnExport = new QPushButton(tr("Als TXT exportieren"), this);
    m_btnExport->setEnabled(false);
    ctrlRow->addWidget(m_btnExport);

    ctrlRow->addStretch();

    m_statusLabel = new QLabel(this);
    m_statusLabel->setStyleSheet("color: palette(mid);");
    ctrlRow->addWidget(m_statusLabel);

    root->addLayout(ctrlRow);

    // ---- Fortschrittsbereich ----
    m_progress = new QProgressBar(this);
    m_progress->setRange(0, 100);
    m_progress->setTextVisible(true);
    m_progress->setFormat("%v / %m Dateien");
    m_progress->hide();
    root->addWidget(m_progress);

    m_progressLabel = new QLabel(this);
    m_progressLabel->setStyleSheet("color: palette(mid); font-size: 11px;");
    m_progressLabel->hide();
    root->addWidget(m_progressLabel);

    // ---- Ergebnistabelle ----
    m_results = new QTreeWidget(this);
    m_results->setColumnCount(4);
    m_results->setHeaderLabels({
        tr("Funktion"),
        tr("Klasse"),
        tr("Datei"),
        tr("Zeile")
    });
    m_results->setRootIsDecorated(false);
    m_results->setAlternatingRowColors(true);
    m_results->setSortingEnabled(true);
    m_results->sortByColumn(0, Qt::AscendingOrder);
    m_results->setSelectionMode(QAbstractItemView::SingleSelection);

    m_results->header()->setSectionResizeMode(0, QHeaderView::Interactive);
    m_results->header()->setSectionResizeMode(1, QHeaderView::Interactive);
    m_results->header()->setSectionResizeMode(2, QHeaderView::Stretch);
    m_results->header()->setSectionResizeMode(3, QHeaderView::Fixed);
    m_results->header()->resizeSection(0, 200);
    m_results->header()->resizeSection(1, 120);
    m_results->header()->resizeSection(3, 60);

    QFont mono("Monospace");
    mono.setStyleHint(QFont::Monospace);
    m_results->setFont(mono);

    root->addWidget(m_results, 1);

    // ---- Verbindungen ----
    connect(m_btnAnalyze, &QPushButton::clicked, this, &DeadCodeDialog::onAnalyze);
    connect(m_btnExport,  &QPushButton::clicked, this, &DeadCodeDialog::onExportClicked);
    connect(m_results, &QTreeWidget::itemActivated,
            this, &DeadCodeDialog::onItemActivated);
}

// ---------------------------------------------------------------------------
// Öffentliche Schnittstelle
// ---------------------------------------------------------------------------
void DeadCodeDialog::setProjectRoot(const QString &path)
{
    // Alte Exclude-Liste für das vorherige Projekt speichern
    if (!m_projectRoot.isEmpty())
    {
        QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
        const QString oldKey = "deadcode/exclude/" +
                               QString(m_projectRoot).replace('/', '_').replace('\\', '_');
        s.setValue(oldKey, m_excludeEdit->text());
    }

    m_projectRoot = path;
    m_results->clear();
    m_statusLabel->setText(QString());
    m_btnExport->setEnabled(false);

    // Exclude-Liste für das neue Projekt laden
    if (!path.isEmpty())
    {
        QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
        const QString key = "deadcode/exclude/" +
                            QString(path).replace('/', '_').replace('\\', '_');

        // Projektspezifisch vorhanden? Sonst globalen Fallback nehmen
        const QString saved = s.value(key,
            s.value("deadcode/excludeDirs", "vendor lib node_modules").toString()
        ).toString();

        m_excludeEdit->setText(saved);
    }
}

// ---------------------------------------------------------------------------
// Analyse starten
// ---------------------------------------------------------------------------
void DeadCodeDialog::onAnalyze()
{
    if (m_projectRoot.isEmpty())
    {
        QMessageBox::information(this, tr("Analyse"),
            tr("Bitte zuerst ein Projekt öffnen."));
        return;
    }

    if (m_watcher->isRunning())
    {
        m_watcher->cancel();
        m_watcher->waitForFinished();
    }

    // Exclude-Liste projektspezifisch speichern
    QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
    const QString key = "deadcode/exclude/" +
                        QString(m_projectRoot).replace('/', '_').replace('\\', '_');
    s.setValue(key, m_excludeEdit->text());
    s.setValue("deadcode/excludeDirs", m_excludeEdit->text()); // globaler Fallback

    const QStringList excludeDirs = m_excludeEdit->text()
                                        .simplified()
                                        .split(' ', Qt::SkipEmptyParts);

    m_results->clear();
    m_btnAnalyze->setEnabled(false);
    m_btnExport->setEnabled(false);
    m_progress->setValue(0);
    m_progress->setMaximum(0); // Unbestimmter Modus bis wir die Dateizahl kennen
    m_progress->show();
    m_progressLabel->setText(tr("Dateien werden gezählt…"));
    m_progressLabel->show();
    m_statusLabel->setText(tr("Analysiere…"));

    const QString     root    = m_projectRoot;
    DeadCodeAnalyzer *analyzer = m_analyzer;

    QFuture<QList<DeadCodeAnalyzer::DeadFunction>> future =
        QtConcurrent::run([analyzer, root, excludeDirs]()
        {
            return analyzer->analyze(root, {"*.php"}, excludeDirs);
        });

    m_watcher->setFuture(future);
}

// ---------------------------------------------------------------------------
// Fortschritt aktualisieren (QueuedConnection — Thread-sicher)
// ---------------------------------------------------------------------------
void DeadCodeDialog::onProgressUpdate(int filesScanned, int filesTotal,
                                       const QString &currentFile)
{
    if (m_progress->maximum() != filesTotal)
    {
        m_progress->setMaximum(filesTotal);
    }
    m_progress->setValue(filesScanned);
    m_progressLabel->setText(tr("(%1 / %2)  %3")
                                 .arg(filesScanned)
                                 .arg(filesTotal)
                                 .arg(currentFile));
}

// ---------------------------------------------------------------------------
// Analyse abgeschlossen
// ---------------------------------------------------------------------------
void DeadCodeDialog::onAnalysisFinished()
{
    m_progress->hide();
    m_progressLabel->hide();
    m_btnAnalyze->setEnabled(true);

    if (m_watcher->isCanceled())
    {
        return;
    }

    const QList<DeadCodeAnalyzer::DeadFunction> dead = m_watcher->result();

    m_results->setSortingEnabled(false);

    for (const DeadCodeAnalyzer::DeadFunction &d : dead)
    {
        QTreeWidgetItem *item = new QTreeWidgetItem(m_results);
        item->setText(0, d.info.signature);
        item->setText(1, d.info.className.isEmpty() ? tr("(global)") : d.info.className);
        item->setText(2, QFileInfo(d.info.filePath).fileName());
        item->setText(3, QString::number(d.info.line));
        item->setToolTip(0, d.reason);
        item->setToolTip(2, d.info.filePath);
        item->setData(0, Qt::UserRole,     d.info.filePath);
        item->setData(0, Qt::UserRole + 1, d.info.line);

        if (d.info.className.isEmpty())
        {
            item->setForeground(1, QColor("#888888"));
        }
    }

    m_results->setSortingEnabled(true);
    m_results->sortByColumn(0, Qt::AscendingOrder);

    if (dead.isEmpty())
    {
        m_statusLabel->setText(tr("✓ Keine ungenutzten Funktionen gefunden."));
        m_statusLabel->setStyleSheet("color: #44bb44;");
    }
    else
    {
        m_statusLabel->setText(tr("%1 Kandidaten gefunden").arg(dead.size()));
        m_statusLabel->setStyleSheet("color: palette(mid);");
        m_btnExport->setEnabled(true);
    }
}

// ---------------------------------------------------------------------------
// Klick auf Treffer
// ---------------------------------------------------------------------------
void DeadCodeDialog::onItemActivated(QTreeWidgetItem *item, int /*column*/)
{
    const QString path = item->data(0, Qt::UserRole).toString();
    const int     line = item->data(0, Qt::UserRole + 1).toInt();
    if (!path.isEmpty() && line > 0)
    {
        emit fileLineRequested(path, line);
    }
}

// ---------------------------------------------------------------------------
// Export
// ---------------------------------------------------------------------------
void DeadCodeDialog::onExportClicked()
{
    const QString path = QFileDialog::getSaveFileName(
        this,
        tr("Ergebnis exportieren"),
        QString("barecode_deadcode_%1.txt")
            .arg(QDateTime::currentDateTime().toString("yyyyMMdd_HHmm")),
        tr("Textdateien (*.txt)")
    );
    if (path.isEmpty()) { return; }

    QFile file(path);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QMessageBox::warning(this, tr("Export"),
            tr("Datei konnte nicht geschrieben werden:\n%1").arg(path));
        return;
    }

    QTextStream out(&file);
    out.setEncoding(QStringConverter::Utf8);

    out << "BareCode – Analyse ungenutzter Funktionen\n";
    out << "Projekt:       " << m_projectRoot << "\n";
    out << "Ausgeschlossen: " << m_excludeEdit->text() << "\n";
    out << "Datum:         " << QDateTime::currentDateTime().toString("dd.MM.yyyy HH:mm") << "\n";
    out << QString("─").repeated(70) << "\n\n";

    for (int i = 0; i < m_results->topLevelItemCount(); ++i)
    {
        QTreeWidgetItem *item = m_results->topLevelItem(i);
        out << item->text(0) << "\n";
        out << "  Klasse:  " << item->text(1) << "\n";
        out << "  Datei:   " << item->toolTip(2) << "\n";
        out << "  Zeile:   " << item->text(3) << "\n\n";
    }

    out << QString("─").repeated(70) << "\n";
    out << m_results->topLevelItemCount() << " Kandidaten\n";
    out << "Hinweis: Dynamische Aufrufe werden nicht erkannt.\n";

    QMessageBox::information(this, tr("Export"),
        tr("Exportiert nach:\n%1").arg(path));
}

// ---------------------------------------------------------------------------
// Fenstergeometrie speichern
// ---------------------------------------------------------------------------
void DeadCodeDialog::closeEvent(QCloseEvent *event)
{
    if (m_watcher->isRunning())
    {
        m_watcher->cancel();
    }
    QSettings s(QSettings::IniFormat, QSettings::UserScope, "BareCode", "BareCode");
    s.setValue("deadcode/geometry", saveGeometry());
    event->accept();
}
