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

#include "ContextManager.hpp"

#include <QFile>
#include <QFileInfo>
#include <QJsonObject>
#include <QTextStream>

#include "settings/GeneralSettings.hpp"
#include <coreplugin/editormanager/editormanager.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectmanager.h>
#include <projectexplorer/projectnodes.h>
#include <texteditor/textdocument.h>

#include "Logger.hpp"

namespace QodeAssist::Context {

ContextManager::ContextManager(QObject *parent)
    : QObject(parent)
    , m_ignoreManager(new IgnoreManager(this))
{}

QString ContextManager::readFile(const QString &filePath) const
{
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        LOG_MESSAGE(QString("Failed to open file for reading: %1 - %2")
                        .arg(filePath, file.errorString()));
        return QString();
    }

    QTextStream in(&file);
    QString content = in.readAll();
    file.close();
    
    return content;
}

QList<ContentFile> ContextManager::getContentFiles(const QStringList &filePaths) const
{
    QList<ContentFile> files;
    for (const QString &path : filePaths) {
        auto project = ProjectExplorer::ProjectManager::projectForFile(
            Utils::FilePath::fromString(path));
        if (project && m_ignoreManager->shouldIgnore(path, project)) {
            LOG_MESSAGE(QString("Ignoring file in context due to .qodeassistignore: %1").arg(path));
            continue;
        }

        ContentFile contentFile = createContentFile(path);
        files.append(contentFile);
    }
    return files;
}

QStringList ContextManager::getProjectSourceFiles(ProjectExplorer::Project *project) const
{
    QStringList sourceFiles;
    if (!project)
        return sourceFiles;

    auto projectNode = project->rootProjectNode();
    if (!projectNode)
        return sourceFiles;

    projectNode->forEachNode(
        [&sourceFiles, this](ProjectExplorer::FileNode *fileNode) {
            if (fileNode /*&& shouldProcessFile(fileNode->filePath().toString())*/) {
                sourceFiles.append(fileNode->filePath().toUrlishString());
            }
        },
        nullptr);

    return sourceFiles;
}

ContentFile ContextManager::createContentFile(const QString &filePath) const
{
    ContentFile contentFile;
    QFileInfo fileInfo(filePath);
    contentFile.filename = fileInfo.fileName();
    contentFile.content = readFile(filePath);
    return contentFile;
}

ProgrammingLanguage ContextManager::getDocumentLanguage(const DocumentInfo &documentInfo) const
{
    if (!documentInfo.document) {
        LOG_MESSAGE("Error: Document is not available for" + documentInfo.filePath);
        return Context::ProgrammingLanguage::Unknown;
    }

    return Context::ProgrammingLanguageUtils::fromMimeType(documentInfo.mimeType);
}

bool ContextManager::isSpecifyCompletion(const DocumentInfo &documentInfo) const
{
    const auto &generalSettings = Settings::generalSettings();

    Context::ProgrammingLanguage documentLanguage = getDocumentLanguage(documentInfo);
    Context::ProgrammingLanguage preset1Language = Context::ProgrammingLanguageUtils::fromString(
        generalSettings.preset1Language.displayForIndex(generalSettings.preset1Language()));

    return generalSettings.specifyPreset1() && documentLanguage == preset1Language;
}

QList<QPair<QString, QString>> ContextManager::openedFiles(const QStringList excludeFiles) const
{
    auto documents = Core::DocumentModel::openedDocuments();

    QList<QPair<QString, QString>> files;

    for (const auto *document : std::as_const(documents)) {
        auto textDocument = qobject_cast<const TextEditor::TextDocument *>(document);
        if (!textDocument)
            continue;

        auto filePath = textDocument->filePath().toUrlishString();

        auto project = ProjectExplorer::ProjectManager::projectForFile(textDocument->filePath());
        if (project && m_ignoreManager->shouldIgnore(filePath, project)) {
            LOG_MESSAGE(
                QString("Ignoring file in context due to .qodeassistignore: %1").arg(filePath));
            continue;
        }

        if (!excludeFiles.contains(filePath)) {
            files.append({filePath, textDocument->plainText()});
        }
    }

    return files;
}

QString ContextManager::openedFilesContext(const QStringList excludeFiles)
{
    QString context = "User files context:\n";

    auto documents = Core::DocumentModel::openedDocuments();

    for (const auto *document : documents) {
        auto textDocument = qobject_cast<const TextEditor::TextDocument *>(document);
        if (!textDocument)
            continue;

        auto filePath = textDocument->filePath().toUrlishString();
        if (excludeFiles.contains(filePath))
            continue;

        auto project = ProjectExplorer::ProjectManager::projectForFile(textDocument->filePath());
        if (project && m_ignoreManager->shouldIgnore(filePath, project)) {
            LOG_MESSAGE(
                QString("Ignoring file in context due to .qodeassistignore: %1").arg(filePath));
            continue;
        }

        context += QString("File: %1\n").arg(filePath);
        context += textDocument->plainText();

        context += "\n";
    }

    return context;
}

IgnoreManager *ContextManager::ignoreManager() const
{
    return m_ignoreManager;
}

} // namespace QodeAssist::Context
