#include "options.h"

#include <saas/library/index_snapshot/snapshot_manager.h>
#include <saas/rtyserver/synchronizer/library/resource.h>

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

int main(int argc, char* argv[]) {
    NLastGetopt::TOpts opts;
    opts.AddHelpOption('h');
    opts.AddVersionOption('V');
    TOptions options;
    options.BindToOpts(opts);

    NLastGetopt::TOptsParseResult res(&opts, argc, argv);
    options.PostProcess(res.GetFreeArgs());

    InitGlobalLog2Console(options.Verbose ? TLOG_INFO : TLOG_CRIT);

    if (options.Command == TOptions::ECommand::ListGroupedResources) {
        options.SnapshotManagerContext.UseGroups = true;
    }

    THolder<IIndexSnapshotManager> manager = THolder(IIndexSnapshotManager::TFactory::Construct(
        options.SnapshotManager, options.SnapshotManagerContext
    ));

    if (options.Command == TOptions::ECommand::ListGroupedResources) {
        TInterval<ui64> shard(options.ShardMin, options.ShardMax);
        const auto [resources, hasPriorResources] = manager->GetGroupedByTSResourcesForShard(shard, TInstant::Seconds(options.Timestamp), TInstant::Max(), IStopCondition::NonStop());

        Cout << "hasPriorResources=" << hasPriorResources << '\n';
        Cout << "resources.size()=" << resources.size() << '\n';

        for (auto& [timestamp, resources] : resources) {
            for (auto& resource : resources) {
                Y_ENSURE(timestamp == TInstant::Seconds(resource.GetTimestamp()));
                Y_ENSURE(resource.GetShardMin() == options.ShardMin);
                Y_ENSURE(resource.GetShardMax() == options.ShardMax);

                Cout << "name=" << resource.GetName()
                    << "\ttimestamp=" << timestamp.Seconds()
                    << "\tshard=" << resource.GetShardMin() << "-" << resource.GetShardMax()
                    << "\ttimestamp_ex=" << resource.GetTimestampEx()
                    << "\tsegment=" << resource.GetSegmentId();
                if (resource.GetTorrent()) {
                    Cout << "\ttorrent=" << resource.GetTorrent();
                }
                if (resource.GetRsync()) {
                    Cout << "\trsync=" << resource.GetRsync();
                }
                if (resource.GetYTIndexTable()) {
                    Cout << "\tytindextable=" << resource.GetYTIndexTable();
                }
                Cout << '\n';
            }
        }

        return 0;
    }

    INFO_LOG << "Getting the list of snapshots at " << options.SnapshotManagerContext.Path.Quote()
             << " on " << options.SnapshotManagerContext.Server.Quote() << Endl;
    auto [snapshots, _] = manager->GetSnapshots();
    INFO_LOG << "Acquired the list of " << snapshots.size() << " snapshot(s)" << Endl;

    auto snapshotIt = snapshots.cbegin();
    if (options.Timestamp) {
        TInstant ts = TInstant::Seconds(options.Timestamp);
        snapshotIt = std::find_if(snapshots.cbegin(), snapshots.cend(), [=](auto& x) { return x.Timestamp == ts; });
        VERIFY_WITH_LOG(snapshotIt != snapshots.cend(), "Cannot find snapshot by timestamp: %lu", options.Timestamp);
    }
    const auto& shards = snapshotIt->Shards.GetShard();

    switch (options.Command) {
        case TOptions::ECommand::ListTimestamps: {
            for (const auto& snapshot : snapshots) {
                Cout << snapshot.Timestamp.Seconds() << Endl;
            }
            break;
        }
        case TOptions::ECommand::ListShards: {
            INFO_LOG << "Found snapshot with timestamp=" << snapshotIt->Timestamp.Seconds() << Endl;
            for (const auto& shard : shards) {
                Cout << shard.GetShardMin() << "-" << shard.GetShardMax() << Endl;
            }
            break;
        }
        case TOptions::ECommand::FetchShard: {
            INFO_LOG << "Found snapshot with timestamp=" << snapshotIt->Timestamp.Seconds() << Endl;
            NRTYServer::TResourceFetchConfig config;
            config.YTFetchConfig.ConstructInPlace();
            config.YTFetchConfig->Proxy = options.SnapshotManagerContext.Server;
            auto shardIt = std::find_if(shards.cbegin(), shards.cend(),
                [=](auto& x) { return (x.GetShardMin() == options.ShardMin && x.GetShardMax() == options.ShardMax); }
            );
            VERIFY_WITH_LOG(shardIt != shards.cend(), "Cannot find shard by shard id: %s", options.ShardId.data());
            INFO_LOG << "Found shard " << options.ShardId << ", fetching into " << options.DstDir.Quote() << Endl;
            NRTYServer::TRemoteResource res(*shardIt);
            res.Fetch(options.DstDir, config);
            INFO_LOG << "Successfuly fetched shard with timestamp=" << snapshotIt->Timestamp.Seconds()
                     << " id=" << options.ShardId << " into " << options.DstDir.Quote() << Endl;
            break;
        }
        default: {
            FAIL_LOG("Unsupported command: %s", ::ToString(options.Command).data());
        }
    }

    return 0;
}
