// SPDX-FileCopyrightText: 2022 - 2023 UnionTech Software Technology Co., Ltd.
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "trashfileinfo.h"
#include "utils/trashcorehelper.h"

#include "dfm-base/interfaces/private/abstractfileinfo_p.h"
#include "dfm-base/dfm_global_defines.h"
#include "dfm-base/base/schemefactory.h"
#include "dfm-base/utils/fileutils.h"
#include "dfm-base/file/local/desktopfileinfo.h"
#include "dfm-base/utils/universalutils.h"
#include "dfm-base/base/standardpaths.h"

#include <dfm-io/denumerator.h>

#include <QCoreApplication>

DFMBASE_USE_NAMESPACE
namespace dfmplugin_trashcore {
class TrashFileInfoPrivate : public AbstractFileInfoPrivate
{
public:
    explicit TrashFileInfoPrivate(const QUrl &url, AbstractFileInfo *qq)
        : AbstractFileInfoPrivate(url, qq)
    {
    }

    virtual ~TrashFileInfoPrivate();

    QUrl initTarget();
    QString fileName() const;
    QString copyName() const;
    QString mimeTypeName();
    QDateTime lastRead() const;
    QDateTime lastModified() const;
    QDateTime deletionTime() const;
    QString symLinkTarget() const;

    QSharedPointer<DFileInfo> dFileInfo { nullptr };
    QSharedPointer<DFileInfo> dAncestorsFileInfo { nullptr };
    QUrl targetUrl;
    QUrl originalUrl;
};

TrashFileInfoPrivate::~TrashFileInfoPrivate()
{
}

QUrl TrashFileInfoPrivate::initTarget()
{
    QVariant attributeTargetUri = dFileInfo->attribute(DFileInfo::AttributeID::kStandardTargetUri);
    if (!attributeTargetUri.toString().isEmpty())
        targetUrl = dFileInfo->attribute(DFileInfo::AttributeID::kStandardTargetUri).toUrl();

    originalUrl = QUrl::fromUserInput(dFileInfo->attribute(DFileInfo::AttributeID::kTrashOrigPath).toString());
    const bool urlIsRoot = UniversalUtils::urlEquals(TrashCoreHelper::rootUrl(), url);
    if (!targetUrl.isValid() && !urlIsRoot) {
        QUrl ancestors = url;
        while (TrashCoreHelper::rootUrl().isParentOf(ancestors)) {
            QUrl urlPre = ancestors;
            ancestors = UrlRoute::urlParent(ancestors);
            if (UniversalUtils::urlEquals(TrashCoreHelper::rootUrl(), ancestors)) {
                ancestors = urlPre;
                break;
            }
        }

        QSharedPointer<DFileInfo> fileinfo { new DFileInfo(ancestors) };
        if (fileinfo->initQuerier()) {
            const QUrl &ancestorsTargetUrl = fileinfo->attribute(DFileInfo::AttributeID::kStandardTargetUri).toUrl();
            if (ancestorsTargetUrl.isValid()) {
                QString localRootPath = ancestorsTargetUrl.toString();
                const QString &fileSuffix = url.path().mid(url.path().indexOf("/", 1));
                const QUrl &urlReal = localRootPath + fileSuffix;

                targetUrl = urlReal;
                QString localRootOriginalPath = fileinfo->attribute(DFileInfo::AttributeID::kTrashOrigPath).toString();
                originalUrl = QUrl::fromUserInput(localRootOriginalPath + fileSuffix);
                dAncestorsFileInfo = fileinfo;
                return urlReal;
            }
        }
    } else if (urlIsRoot) {
        const QUrl &urlTrashFiles = QUrl::fromLocalFile(StandardPaths::location(StandardPaths::kTrashLocalFilesPath));
        QSharedPointer<DFileInfo> fileinfo { new DFileInfo(urlTrashFiles) };
        if (fileinfo->initQuerier()) {
            targetUrl = urlTrashFiles;
            originalUrl = QUrl();
            dAncestorsFileInfo = fileinfo;
            return targetUrl;
        }
    }

    return targetUrl;
}

QString TrashFileInfoPrivate::fileName() const
{
    if (!dFileInfo)
        return QString();

    if (targetUrl.isValid()) {
        if (FileUtils::isDesktopFile(targetUrl)) {
            DesktopFileInfo dfi(targetUrl);
            return dfi.displayOf(DisPlayInfoType::kFileDisplayName);
        }
    }

    return dFileInfo->attribute(DFileInfo::AttributeID::kStandardName).toString();
}

QString TrashFileInfoPrivate::copyName() const
{
    if (!dFileInfo)
        return QString();

    if (targetUrl.isValid()) {
        if (FileUtils::isDesktopFile(targetUrl)) {
            DesktopFileInfo dfi(targetUrl);
            return dfi.nameOf(NameInfoType::kFileCopyName);
        }
    }

    return dFileInfo->attribute(DFileInfo::AttributeID::kStandardCopyName).toString();
}

QString TrashFileInfoPrivate::mimeTypeName()
{
    if (!dFileInfo)
        return QString();

    QString type;
    bool success = false;
    type = dFileInfo->attribute(DFileInfo::AttributeID::kStandardContentType, &success).toString();
    return type;
}

QDateTime TrashFileInfoPrivate::lastRead() const
{
    if (!dFileInfo)
        return QDateTime();

    QDateTime time;
    bool success = false;
    uint64_t data = dFileInfo->attribute(DFileInfo::AttributeID::kTimeAccess, &success).value<uint64_t>();
    if (success) {
        time = QDateTime::fromTime_t(static_cast<uint>(data));
    } else {
        if (dAncestorsFileInfo)
            time = QDateTime::fromTime_t(static_cast<uint>(dAncestorsFileInfo->attribute(DFileInfo::AttributeID::kTimeAccess, &success).value<uint64_t>()));
    }
    return time;
}

QDateTime TrashFileInfoPrivate::lastModified() const
{
    if (!dFileInfo)
        return QDateTime();

    QDateTime time;
    bool success = false;
    uint64_t data = dFileInfo->attribute(DFileInfo::AttributeID::kTimeModified, &success).value<uint64_t>();
    if (success) {
        time = QDateTime::fromTime_t(static_cast<uint>(data));
    } else {
        if (dAncestorsFileInfo)
            time = QDateTime::fromTime_t(static_cast<uint>(dAncestorsFileInfo->attribute(DFileInfo::AttributeID::kTimeModified, &success).value<uint64_t>()));
    }
    return time;
}

QDateTime TrashFileInfoPrivate::deletionTime() const
{
    if (dAncestorsFileInfo)
        return QDateTime::fromString(dAncestorsFileInfo->attribute(DFileInfo::AttributeID::kTrashDeletionDate).toString(), Qt::ISODate);

    if (!dFileInfo)
        return QDateTime();

    return QDateTime::fromString(dFileInfo->attribute(DFileInfo::AttributeID::kTrashDeletionDate).toString(), Qt::ISODate);
}

TrashFileInfo::TrashFileInfo(const QUrl &url)
    : AbstractFileInfo(url), d(new TrashFileInfoPrivate(url, this))
{
    dptr.reset(d);

    d->dFileInfo.reset(new DFileInfo(url));
    if (!d->dFileInfo) {
        qWarning() << "dfm-io use factory create fileinfo Failed, url: " << url;
        return;
    }
    bool init = d->dFileInfo->initQuerier();
    if (!init) {
        qWarning() << "querier init failed, url: " << url;
        return;
    }

    const QUrl &urlTarget = d->initTarget();
    if (urlTarget.isValid()) {
        setProxy(InfoFactory::create<AbstractFileInfo>(urlTarget));
    } else {
        if (!FileUtils::isTrashRootFile(url))
            qWarning() << "create proxy failed, target url is invalid, url: " << url;
    }
}

TrashFileInfo::~TrashFileInfo()
{
}

bool TrashFileInfo::exists() const
{
    return AbstractFileInfo::exists()
            || FileUtils::isTrashRootFile(urlOf(UrlInfoType::kUrl));
}

Qt::DropActions TrashFileInfo::supportedOfAttributes(const AbstractFileInfo::SupportType type) const
{
    switch (type) {
    case AbstractFileInfo::SupportType::kDrop: {
        const QString &path = d->url.path();

        return path.isEmpty() || path == "/" ? Qt::MoveAction : Qt::IgnoreAction;
    }
    case AbstractFileInfo::SupportType::kDrag:
        return Qt::CopyAction | Qt::MoveAction;
    default:
        return AbstractFileInfo::supportedOfAttributes(type);
    }
}

void TrashFileInfo::refresh()
{
    AbstractFileInfo::refresh();
}

QString TrashFileInfo::nameOf(const NameInfoType type) const
{
    switch (type) {
    case NameInfoType::kFileName:
        return d->fileName();
    case NameInfoType::kFileCopyName: {
        if (d->targetUrl.isValid()) {
            if (FileUtils::isDesktopFile(d->targetUrl)) {
                return d->copyName();
            }
        }
        return displayOf(DisPlayInfoType::kFileDisplayName);
    }
    case NameInfoType::kMimeTypeName:
        return const_cast<TrashFileInfoPrivate *>(d)->mimeTypeName();
    default:
        return AbstractFileInfo::nameOf(type);
    }
}

QString TrashFileInfo::displayOf(const DisPlayInfoType type) const
{
    if (DisPlayInfoType::kFileDisplayName == type) {
        if (urlOf(UrlInfoType::kUrl) == TrashCoreHelper::rootUrl())
            return QCoreApplication::translate("PathManager", "Trash");

        if (!d->dFileInfo)
            return QString();

        if (d->targetUrl.isValid()) {
            if (FileUtils::isDesktopFile(d->targetUrl)) {
                DesktopFileInfo dfi(d->targetUrl);
                return dfi.displayOf(DisPlayInfoType::kFileDisplayName);
            }
        }

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kStandardDisplayName).toString();
    }

    return AbstractFileInfo::displayOf(type);
}
QString TrashFileInfo::pathOf(const PathInfoType type) const
{
    switch (type) {
    case FilePathInfoType::kSymLinkTarget:
        return d->symLinkTarget();
    default:
        return AbstractFileInfo::pathOf(type);
    }
}

QUrl TrashFileInfo::urlOf(const UrlInfoType type) const
{
    switch (type) {
    case FileUrlInfoType::kRedirectedFileUrl:
        return d->targetUrl;
    case FileUrlInfoType::kOriginalUrl:
        return d->originalUrl;
    default:
        return AbstractFileInfo::urlOf(type);
    }
}

bool TrashFileInfo::canAttributes(const CanableInfoType type) const
{
    switch (type) {
    case FileCanType::kCanDelete:
        if (!d->dFileInfo)
            return false;

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kAccessCanDelete, nullptr).toBool();
    case FileCanType::kCanTrash:
        if (!d->dFileInfo)
            return false;

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kAccessCanTrash, nullptr).toBool();
    case FileCanType::kCanRename:
        if (!d->dFileInfo)
            return false;

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kAccessCanRename, nullptr).toBool();
    case FileCanType::kCanDrop:
        return FileUtils::isTrashRootFile(urlOf(UrlInfoType::kUrl));
    case FileCanType::kCanHidden:
        return false;
    case FileCanType::kCanRedirectionFileUrl:
        return true;
    default:
        return AbstractFileInfo::canAttributes(type);
    }
}

QFile::Permissions TrashFileInfo::permissions() const
{
    QFileDevice::Permissions p = AbstractFileInfo::permissions();

    p &= ~QFileDevice::WriteOwner;
    p &= ~QFileDevice::WriteUser;
    p &= ~QFileDevice::WriteGroup;
    p &= ~QFileDevice::WriteOther;

    return p;
}

QIcon TrashFileInfo::fileIcon()
{
    if (d->targetUrl.isValid()) {
        if (FileUtils::isDesktopFile(d->targetUrl)) {
            DesktopFileInfo dfi(d->targetUrl);
            return dfi.fileIcon();
        }
    }

    return AbstractFileInfo::fileIcon();
}

qint64 TrashFileInfo::size() const
{
    if (!d->dFileInfo)
        return qint64();

    qint64 size = 0;
    const QUrl &fileUrl = urlOf(UrlInfoType::kUrl);
    if (FileUtils::isTrashRootFile(fileUrl)) {
        auto data = TrashCoreHelper::calculateTrashRoot();
        return data.first;
    }

    bool success = false;
    size = d->dFileInfo->attribute(DFileInfo::AttributeID::kStandardSize, &success).value<qint64>();
    return size;
}

QString TrashFileInfoPrivate::symLinkTarget() const
{
    if (!dFileInfo)
        return QString();

    QString symLinkTarget;
    bool success = false;
    symLinkTarget = dFileInfo->attribute(DFileInfo::AttributeID::kStandardSymlinkTarget, &success).toString();
    return symLinkTarget;
}

int TrashFileInfo::countChildFile() const
{
    if (FileUtils::isTrashRootFile(urlOf(UrlInfoType::kUrl))) {
        if (d->dFileInfo)
            return d->dFileInfo->attribute(DFMIO::DFileInfo::AttributeID::kTrashItemCount).toInt();
    }

    if (isAttributes(OptInfoType::kIsDir)) {
        DFMIO::DEnumerator enumerator(urlOf(UrlInfoType::kUrl));
        return int(enumerator.fileCount());
    }

    return -1;
}

bool TrashFileInfo::isAttributes(const OptInfoType type) const
{
    switch (type) {
    case FileIsType::kIsDir:
        if (FileUtils::isTrashRootFile(urlOf(UrlInfoType::kUrl)))
            return true;
        return AbstractFileInfo::isAttributes(type);
    case FileIsType::kIsReadable:
        if (!d->dFileInfo)
            return false;

        if (d->targetUrl.isValid())
            return AbstractFileInfo::isAttributes(OptInfoType::kIsReadable);

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kAccessCanRead, nullptr).toBool();
    case FileIsType::kIsWritable:
        if (!d->dFileInfo)
            return false;

        if (d->targetUrl.isValid())
            return AbstractFileInfo::isAttributes(type);

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kAccessCanWrite, nullptr).toBool();
    case FileIsType::kIsHidden:
        return false;
    case FileIsType::kIsSymLink:
        if (!d->dFileInfo)
            return false;

        return d->dFileInfo->attribute(DFileInfo::AttributeID::kStandardIsSymlink, nullptr).toBool();
    default:
        return AbstractFileInfo::isAttributes(type);
    }
}

QVariant TrashFileInfo::timeOf(const TimeInfoType type) const
{
    switch (type) {
    case TimeInfoType::kLastRead:
        return d->lastRead();
    case TimeInfoType::kLastModified:
        return d->lastModified();
    case TimeInfoType::kDeletionTime:
        return d->deletionTime();
    default:
        return AbstractFileInfo::timeOf(type);
    }
}

QVariant TrashFileInfo::customData(int role) const
{
    using namespace dfmbase::Global;
    if (role == kItemFileOriginalPath)
        return urlOf(UrlInfoType::kOriginalUrl).path();
    else if (role == kItemFileDeletionDate)
        return d->deletionTime().toString(FileUtils::dateTimeFormat());
    else
        return QVariant();
}

}
