#include "gitfilesystemmodel.h"
#include <QProcess>
#include <QDir>
#include <QFileInfo>

GitFileSystemModel::GitFileSystemModel(QObject *parent)
    : QFileSystemModel(parent)
{
    setFilter(QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
    connect(this, &QFileSystemModel::directoryLoaded, this, &GitFileSystemModel::onDirectoryLoaded);
    connect(this, &QFileSystemModel::rootPathChanged, this, [this](const QString &path){
        updateGitStatus(path);
        emit rootIndexChanged();
    });

    _refreshTimer.setInterval(5000);
    connect(&_refreshTimer, &QTimer::timeout, this, &GitFileSystemModel::refreshGitStatus);
    _refreshTimer.start();
}

void GitFileSystemModel::onDirectoryLoaded(const QString &path) {
    updateGitStatus(path);
    updateGitIgnore(path);
}

void GitFileSystemModel::refreshGitStatus() {
    updateGitStatus(rootPath());
    updateGitIgnore(rootPath());
}

bool GitFileSystemModel::event(QEvent *event) {
    return QFileSystemModel::event(event);
}

QVariant GitFileSystemModel::getRootIndex() const {
    return QVariant::fromValue(index(rootPath()));
}

QString GitFileSystemModel::findGitRepo(const QString &path) const {
    QDir dir(path);
    while (dir.exists()) {
        if (QFileInfo(dir.filePath(".git")).exists()) {
            return dir.absolutePath();
        }
        if (!dir.cdUp()) {
            break;
        }
    }
    return QString();
}

void GitFileSystemModel::updateGitStatus(const QString &repoPath) {
    QString gitPath = findGitRepo(repoPath);
    if (gitPath.isEmpty()) {
        _gitStatuses.clear();
        return; // emit dataChanged maybe?
    }

    QProcess process;
    process.setWorkingDirectory(gitPath);
    process.start("git", QStringList() << "status" << "--porcelain");
    process.waitForFinished(2000);

    QString output = process.readAllStandardOutput();
    QStringList lines = output.split('\n', Qt::SkipEmptyParts);

    QMap<QString, QString> newStatuses;
    for (const QString &line : lines) {
        if (line.length() > 3) {
            QString status = line.left(2);
            QString file = line.mid(3).trimmed();
            newStatuses[QDir(gitPath).filePath(file)] = status;
        }
    }

    if (_gitStatuses != newStatuses) {
        _gitStatuses = newStatuses;
        // Ideally emit dataChanged for all visible indices.
        emit dataChanged(index(rootPath()), index(rootPath()), {GitStatusRole});
    }
}

QHash<int, QByteArray> GitFileSystemModel::roleNames() const {
    QHash<int, QByteArray> roles = QFileSystemModel::roleNames();
    roles[GitStatusRole] = "gitStatus";
    roles[IsFileRole] = "isFile";
    roles[IsDirectoryRole] = "isDirectory";
    roles[IsGitIgnoredRole] = "isGitIgnored";
    return roles;
}

QVariant GitFileSystemModel::data(const QModelIndex &index, int role) const {
    if (role == GitStatusRole) {
        QString path = filePath(index);
        return _gitStatuses.value(path, "");
    } else if (role == IsFileRole) {
        return QFileInfo(filePath(index)).isFile();
    } else if (role == IsDirectoryRole) {
        return QFileInfo(filePath(index)).isDir();
    } else if (role == IsGitIgnoredRole) {
        QString path = filePath(index);
        if (_gitIgnored.contains(path))
            return true;
        // Check if any parent directory is ignored
        for (const QString &ignored : _gitIgnored) {
            if (path.startsWith(ignored + "/"))
                return true;
        }
        return false;
    }
    return QFileSystemModel::data(index, role);
}

void GitFileSystemModel::updateGitIgnore(const QString &repoPath) {
    QString gitPath = findGitRepo(repoPath);
    if (gitPath.isEmpty()) {
        _gitIgnored.clear();
        return;
    }

    QProcess process;
    process.setWorkingDirectory(gitPath);
    process.start("git", QStringList() << "ls-files" << "--others" << "--ignored" << "--exclude-standard" << "--directory");
    process.waitForFinished(3000);

    QString output = process.readAllStandardOutput();
    QStringList lines = output.split('\n', Qt::SkipEmptyParts);

    QSet<QString> newIgnored;
    QDir gitDir(gitPath);
    for (const QString &line : lines) {
        QString trimmed = line.trimmed();
        if (trimmed.endsWith('/'))
            trimmed.chop(1);
        newIgnored.insert(gitDir.filePath(trimmed));
    }

    // Always hide .git directory itself
    newIgnored.insert(gitDir.filePath(".git"));

    if (_gitIgnored != newIgnored) {
        _gitIgnored = newIgnored;
        emit dataChanged(index(rootPath()), index(rootPath()), {IsGitIgnoredRole});
    }
}

