#include "FileSearchPanel.h"

#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QTextStream>
#include <QRegularExpression>
#include <QKeyEvent>
#include <QtConcurrent/QtConcurrent>
#include <QFuture>

// ---------------------------------------------------------------------------
// Konstruktor
// ---------------------------------------------------------------------------
FileSearchPanel::FileSearchPanel(QWidget *parent)
    : QWidget(parent)
{
    setupUi();
    hide();

    m_watcher = new QFutureWatcher<QList<Match>>(this);
    connect(m_watcher, &QFutureWatcher<QList<Match>>::finished,
            this, &FileSearchPanel::onSearchFinished);
}

// ---------------------------------------------------------------------------
// UI
// ---------------------------------------------------------------------------
void FileSearchPanel::setupUi()
{
    QVBoxLayout *root = new QVBoxLayout(this);
    root->setContentsMargins(6, 4, 6, 4);
    root->setSpacing(4);

    // ---- Zeile 1: Suchbegriff ----
    QHBoxLayout *row1 = new QHBoxLayout();
    row1->addWidget(new QLabel(tr("Suchen in Dateien:"), this));

    m_searchEdit = new QLineEdit(this);
    m_searchEdit->setPlaceholderText(tr("Suchbegriff…"));
    m_searchEdit->setClearButtonEnabled(true);
    row1->addWidget(m_searchEdit, 1);

    m_btnSearch = new QPushButton(tr("Suchen"), this);
    m_btnSearch->setDefault(true);
    row1->addWidget(m_btnSearch);

    m_btnClose = new QPushButton(tr("✕"), this);
    m_btnClose->setFixedWidth(24);
    m_btnClose->setFlat(true);
    m_btnClose->setToolTip(tr("Schließen"));
    row1->addWidget(m_btnClose);

    root->addLayout(row1);

    // ---- Zeile 2: Optionen + Filter ----
    QHBoxLayout *row2 = new QHBoxLayout();
    m_chkCase  = new QCheckBox(tr("Groß-/Kleinschreibung"), this);
    m_chkWord  = new QCheckBox(tr("Ganzes Wort"), this);
    m_chkRegex = new QCheckBox(tr("Regex"), this);

    row2->addWidget(m_chkCase);
    row2->addWidget(m_chkWord);
    row2->addWidget(m_chkRegex);
    row2->addSpacing(12);

    row2->addWidget(new QLabel(tr("Dateitypen:"), this));
    m_filterEdit = new QLineEdit(this);
    m_filterEdit->setText("*.html *.php *.css *.js *.c *.cpp *.h");
    m_filterEdit->setFixedWidth(220);
    m_filterEdit->setToolTip(tr("Leerzeichen-getrennte Muster, z.B.: *.php *.html"));
    row2->addWidget(m_filterEdit);
    row2->addStretch();

    root->addLayout(row2);

    // ---- Fortschritt + Status ----
    m_progress = new QProgressBar(this);
    m_progress->setRange(0, 0);   // Unbestimmter Modus
    m_progress->setFixedHeight(4);
    m_progress->hide();
    root->addWidget(m_progress);

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

    // ---- Ergebnisliste ----
    m_results = new QTreeWidget(this);
    m_results->setHeaderHidden(true);
    m_results->setRootIsDecorated(true);
    m_results->setIndentation(16);
    m_results->setUniformRowHeights(true);
    m_results->setAlternatingRowColors(true);
    root->addWidget(m_results, 1);

    // ---- Verbindungen ----
    connect(m_btnSearch, &QPushButton::clicked, this, &FileSearchPanel::onSearch);
    connect(m_searchEdit, &QLineEdit::returnPressed, this, &FileSearchPanel::onSearch);
    connect(m_btnClose, &QPushButton::clicked, this, [this]()
    {
        hide();
    });
    connect(m_results, &QTreeWidget::itemActivated,
            this, &FileSearchPanel::onResultActivated);
}

// ---------------------------------------------------------------------------
// Öffentliche Schnittstelle
// ---------------------------------------------------------------------------
void FileSearchPanel::setSearchRoot(const QString &path)
{
    m_searchRoot = path;
}

void FileSearchPanel::activate()
{
    show();
    m_searchEdit->setFocus();
    m_searchEdit->selectAll();
}

// ---------------------------------------------------------------------------
// Suche starten
// ---------------------------------------------------------------------------
void FileSearchPanel::onSearch()
{
    const QString needle = m_searchEdit->text().trimmed();
    if (needle.isEmpty())
    {
        return;
    }

    if (m_searchRoot.isEmpty())
    {
        m_statusLabel->setText(tr("Kein Projektverzeichnis geöffnet."));
        return;
    }

    // Laufende Suche abbrechen
    if (m_watcher->isRunning())
    {
        m_watcher->cancel();
        m_watcher->waitForFinished();
    }

    m_results->clear();
    m_statusLabel->setText(tr("Suche läuft…"));
    m_progress->show();
    m_btnSearch->setEnabled(false);

    const QString      root       = m_searchRoot;
    const bool         cs         = m_chkCase->isChecked();
    const bool         word       = m_chkWord->isChecked();
    const bool         regex      = m_chkRegex->isChecked();
    const QStringList  extensions = m_filterEdit->text().simplified().split(' ',
                                        Qt::SkipEmptyParts);

    QFuture<QList<Match>> future = QtConcurrent::run(
        [this, root, needle, cs, word, regex, extensions]()
        {
            return searchInFiles(root, needle, cs, word, regex, extensions);
        }
    );

    m_watcher->setFuture(future);
}

// ---------------------------------------------------------------------------
// Suchergebnisse anzeigen
// ---------------------------------------------------------------------------
void FileSearchPanel::onSearchFinished()
{
    m_progress->hide();
    m_btnSearch->setEnabled(true);

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

    const QList<Match> matches = m_watcher->result();

    // Ergebnisse gruppiert nach Datei aufbauen
    QString      currentFile;
    QTreeWidgetItem *fileItem = nullptr;
    int          fileCount   = 0;
    int          matchCount  = 0;

    for (const Match &m : matches)
    {
        if (m.filePath != currentFile)
        {
            currentFile = m.filePath;
            ++fileCount;

            fileItem = new QTreeWidgetItem(m_results);
            fileItem->setText(0, QFileInfo(m.filePath).fileName());
            fileItem->setToolTip(0, m.filePath);
            fileItem->setData(0, Qt::UserRole, m.filePath);
            fileItem->setData(0, Qt::UserRole + 1, -1);

            QFont boldFont = fileItem->font(0);
            boldFont.setBold(true);
            fileItem->setFont(0, boldFont);
            fileItem->setExpanded(true);
        }

        QTreeWidgetItem *lineItem = new QTreeWidgetItem(fileItem);
        lineItem->setText(0, QString("  Zeile %1: %2")
                              .arg(m.line)
                              .arg(m.content.trimmed().left(120)));
        lineItem->setToolTip(0, m.content.trimmed());
        lineItem->setData(0, Qt::UserRole,     m.filePath);
        lineItem->setData(0, Qt::UserRole + 1, m.line);

        ++matchCount;
    }

    // Datei-Titelzeilen um Trefferanzahl ergänzen
    for (int i = 0; i < m_results->topLevelItemCount(); ++i)
    {
        QTreeWidgetItem *item = m_results->topLevelItem(i);
        const int count       = item->childCount();
        item->setText(0, QString("%1  (%2 Treffer)")
                           .arg(QFileInfo(item->data(0, Qt::UserRole).toString()).fileName())
                           .arg(count));
    }

    if (matchCount == 0)
    {
        m_statusLabel->setText(tr("Keine Treffer gefunden."));
    }
    else
    {
        m_statusLabel->setText(tr("%1 Treffer in %2 Datei(en).")
                                   .arg(matchCount)
                                   .arg(fileCount));
    }
}

// ---------------------------------------------------------------------------
// Klick auf Treffer → Datei + Zeile öffnen
// ---------------------------------------------------------------------------
void FileSearchPanel::onResultActivated(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)
    {
        // Datei-Titelzeile: nur auf-/zuklappen
        item->setExpanded(!item->isExpanded());
        return;
    }

    emit fileLineRequested(path, line);
}

// ---------------------------------------------------------------------------
// Eigentliche Suchroutine (läuft in Thread-Pool)
// ---------------------------------------------------------------------------
QList<FileSearchPanel::Match> FileSearchPanel::searchInFiles(
    const QString    &root,
    const QString    &needle,
    bool              caseSensitive,
    bool              wholeWord,
    bool              useRegex,
    const QStringList &extensions) const
{
    QList<Match> results;

    // Regulären Ausdruck vorbereiten
    QString pattern = useRegex ? needle : QRegularExpression::escape(needle);
    if (wholeWord)
    {
        pattern = "\\b" + pattern + "\\b";
    }

    QRegularExpression re(pattern,
        caseSensitive
            ? QRegularExpression::NoPatternOption
            : QRegularExpression::CaseInsensitiveOption);

    if (!re.isValid())
    {
        return results;
    }

    // Verzeichnis rekursiv durchsuchen
    QDirIterator it(root,
                    extensions.isEmpty()
                        ? QStringList("*")
                        : extensions,
                    QDir::Files,
                    QDirIterator::Subdirectories);

    while (it.hasNext())
    {
        if (m_watcher->isCanceled())
        {
            break;
        }

        const QString filePath = it.next();

        QFile file(filePath);
        if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        {
            continue;
        }

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

        int lineNumber = 0;
        while (!stream.atEnd())
        {
            ++lineNumber;
            const QString line = stream.readLine();

            if (re.match(line).hasMatch())
            {
                results.append({ filePath, lineNumber, line });
            }
        }
    }

    return results;
}
