// Copyright (C) 2025-2026 Petr Mironychev
// SPDX-License-Identifier: GPL-3.0-or-later
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE

#include "ListProjectFilesTool.hpp"

#include <LLMQore/ToolExceptions.hpp>

#include <logger/Logger.hpp>
#include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
#include <QDir>
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonObject>
#include <QtConcurrent>

namespace QodeAssist::Tools {

ListProjectFilesTool::ListProjectFilesTool(QObject *parent)
    : BaseTool(parent)
    , m_ignoreManager(new Context::IgnoreManager(this))

{}

QString ListProjectFilesTool::id() const
{
    return "list_project_files";
}

QString ListProjectFilesTool::displayName() const
{
    return {"Reading project files list"};
}

QString ListProjectFilesTool::description() const
{
    return "List every source file tracked by the active Qt Creator project(s), filtered by "
           ".qodeassistignore. Returns absolute and project-relative paths grouped by project. "
           "Useful for discovering the project layout before running focused searches or reads. "
           "Takes no parameters.";
}

QJsonObject ListProjectFilesTool::parametersSchema() const
{
    QJsonObject definition;
    definition["type"] = "object";
    definition["properties"] = QJsonObject();
    definition["required"] = QJsonArray();

    return definition;
}

QFuture<LLMQore::ToolResult> ListProjectFilesTool::executeAsync(const QJsonObject &input)
{
    Q_UNUSED(input)

    return QtConcurrent::run([this]() -> LLMQore::ToolResult {
        QList<ProjectExplorer::Project *> projects = ProjectExplorer::ProjectManager::projects();
        if (projects.isEmpty()) {
            QString error = "No projects found";
            throw LLMQore::ToolRuntimeError(error);
        }

        QString result;

        for (auto project : projects) {
            if (!project)
                continue;

            Utils::FilePaths projectFiles = project->files(ProjectExplorer::Project::SourceFiles);

            if (projectFiles.isEmpty()) {
                result += QString("Project '%1': No source files found\n\n")
                              .arg(project->displayName());
                continue;
            }

            QStringList fileList;
            QString projectPath = project->projectDirectory().toUrlishString();
            QString projectAbsolutePath = project->projectDirectory().toFSPathString();

            for (const auto &filePath : projectFiles) {
                QString absolutePath = filePath.toUrlishString();

                if (m_ignoreManager->shouldIgnore(absolutePath, project)) {
                    LOG_MESSAGE(
                        QString("Ignoring file due to .qodeassistignore: %1").arg(absolutePath));
                    continue;
                }

                QString relativePath = QDir(projectPath).relativeFilePath(absolutePath);
                fileList.append(relativePath);
            }

            if (fileList.isEmpty()) {
                result += QString("Project '%1': No files after applying .qodeassistignore\n\n")
                              .arg(project->displayName());
                continue;
            }

            fileList.sort();

            result += QString("Project '%1' (%2 files):\n")
                          .arg(project->displayName())
                          .arg(fileList.size());
            result += QString("Project root: %1\n\n").arg(projectAbsolutePath);
            for (const QString &file : fileList) {
                result += QString("- %1\n").arg(file);
            }
            result += "\n";
        }

        return LLMQore::ToolResult::text(result.trimmed());
    });
}

QString ListProjectFilesTool::formatFileList(const QStringList &files) const
{
    QString result = QString("Project files (%1 total):\n\n").arg(files.size());

    for (const QString &file : files) {
        result += QString("- %1\n").arg(file);
    }

    return result;
}

} // namespace QodeAssist::Tools
