#include "resource.h"

#include <saas/library/yt/file/yt_file_fetcher.h>
#include <saas/util/system/copy_recursive.h>

#include <robot/library/oxygen/base/system/rsync.h>
#include <util/system/fs.h>

#include <library/cpp/logger/global/global.h>

namespace {
    const ui32 RETRY_COUNT = 3;
    const TDuration RETRY_DELAY = TDuration::Minutes(1);
}

NRTYServer::TRemoteResource::TRemoteResource(const TString& description)
    : Name(description)
{
    if (description.StartsWith("rbtorrent")) {
        SetTorrent(description);
    } else if (description.StartsWith("rsync")) {
        SetRsync(description);
    } else if (description.StartsWith("yt")) {
        SetYTIndexTable(description);
    } else {
        SetLocalPath(description);
    }
}

bool NRTYServer::TRemoteResource::Fetch(const TFsPath& destination, const NRTYServer::TResourceFetchConfig& config,
    IRemoteResourceGetCallback* callback) const
{
    if (!LocalPath && !Torrent && !Rsync && !YTIndexTable) {
        WARNING_LOG << "Fetch resource failed: sources are not specified." << Endl;
        return false;
    }
    if (LocalPath) {
        for (ui32 tries = 0; tries < RETRY_COUNT; ++tries) {
            if (FetchFromLocalPath(destination)) {
                return true;
            }
            Sleep(RETRY_DELAY);
        }
    }
    if (Torrent) {
        NOxygen::TSkyGetConfig skyNetConfig = NOxygen::TSkyGetConfig();
        if (config.SkyGetConfig) {
            skyNetConfig.SetDownloadSpeedLimitBytes(config.SkyGetConfig->DownloadSpeedBps)
            .SetUploadSpeedLimitBytes(config.SkyGetConfig->UploadSpeedBps)
            .SetTimeout(TDuration::Seconds(config.SkyGetConfig->TimeoutSeconds));
        }

        for (auto networkType : {NOxygen::ESkyNetwork::FASTBONE, NOxygen::ESkyNetwork::AUTO, NOxygen::ESkyNetwork::BACKBONE}) {
            skyNetConfig.SetNetwork(networkType);
            const auto retryCount = networkType == NOxygen::ESkyNetwork::FASTBONE ? RETRY_COUNT : 1;
            for (ui32 tries = 0; tries < retryCount; ++tries) {
                if (callback)
                    callback->OnSkyGetStart(tries);
                if (FetchFromTorrent(destination, skyNetConfig, callback, /*cleanOnError =*/ false)) {
                    return true;
                }
                Sleep(RETRY_DELAY);
            }
        }
        destination.ForceDelete();
    }
    if (Rsync) {
        for (ui32 tries = 0; tries < RETRY_COUNT; ++tries) {
            if (FetchFromRsync(destination)) {
                return true;
            }
            Sleep(RETRY_DELAY);
        }
    }
    if (YTIndexTable) {
        if (!config.YTFetchConfig) {
            WARNING_LOG << "Fetch resource failed: there's no <YTFetch> section in the config" << Endl;
            return false;
        }
        for (ui32 tries = 0; tries < RETRY_COUNT; ++tries) {
            if (FetchFromYTIndexTable(destination, *config.YTFetchConfig)) {
                return true;
            }
            Sleep(RETRY_DELAY);
        }
    }
    WARNING_LOG << "Fetch resource failed." << Endl;
    return false;
}

bool NRTYServer::TRemoteResource::FetchFromTorrent(const TFsPath& destination, const NOxygen::TSkyGetConfig& config,
        IRemoteResourceGetCallback* callback, bool cleanOnError) const
{
    try {
        NOxygen::SkyGet(destination, Torrent, config, callback);
    } catch (const yexception& e) {
        if (callback)
            callback->OnSkyGetError(e.what());
        WARNING_LOG << "Cannot download resource from torrent: " << e.what() << Endl;
        if (cleanOnError) {
            destination.ForceDelete();
        }
        return false;
    }
    return true;
}
bool NRTYServer::TRemoteResource::FetchFromRsync(const TFsPath& destination) const {
    try {
        NOxygen::DoRsync(Rsync, destination);
    } catch (const yexception& e) {
        WARNING_LOG << "Cannot download resource from rsync: " << e.what() << Endl;
        destination.ForceDelete();
        return false;
    }
    return true;
}
bool NRTYServer::TRemoteResource::FetchFromYTIndexTable(const TFsPath& destination,
    const NRTYServer::TResourceFetchConfig::TYTFetchConfig& config) const
{
    try {
        size_t pathStart = YTIndexTable.find("//");
        if (pathStart == TString::npos) {
            WARNING_LOG << "Cannot download resource from YT: invalid path: " << YTIndexTable.Quote() << Endl;
            return false;
        }
        TYTFileFetcher fetcher(config.Proxy, config.Token, config.YTHosts);
        fetcher.Fetch(YTIndexTable.substr(pathStart), destination, config.WriteBytesPerSec);
    } catch (const yexception& e) {
        WARNING_LOG << "Cannot download resource from YT: " << e.what() << Endl;
        destination.ForceDelete();
        return false;
    }
    return true;
}
bool NRTYServer::TRemoteResource::FetchFromLocalPath(const TFsPath& destination) const {
    if (NFs::SymLink(destination, LocalPath)) {
        return true;
    }
    WARNING_LOG << "Cannot create SymLink to " << LocalPath << Endl;
    WARNING_LOG << "Copying " << LocalPath << " to " << destination << Endl;
    try {
        NUtil::CopyRecursive(LocalPath, destination);
    } catch (const yexception& e) {
        WARNING_LOG << "Cannot copy to " << destination << ": " << e.what() << Endl;
        destination.ForceDelete();
        return false;
    }
    return true;
}

