#include "adapters/eft/items/catalog/CatalogRebuildJob.h"

#include <algorithm>
#include <string>
#include <utility>

#include "adapters/eft/localization/EftLocalizationService.h"
#include "foundation/diagnostics/BuildOptions.h"
#include "foundation/diagnostics/Logger.h"

namespace ta::adapters::eft::items
{
    CatalogRebuildJob::CatalogRebuildJob(const CatalogRetryPolicy retry_policy)
        : retry_policy_(retry_policy)
    {
    }

    void CatalogRebuildJob::update(Il2CppObject* factory)
    {
        if (!factory)
        {
            phase_ = CatalogPhase::WaitingForFactory;
            return;
        }
        if (factory != factory_)
            reset_for_factory(factory);

        if (reload_requested_ && phase_ != CatalogPhase::RetryDelay)
        {
            if (!begin_rebuild())
                schedule_retry();
        }

        switch (phase_)
        {
        case CatalogPhase::RetryDelay:
            if (retry_frames_ > 0)
                --retry_frames_;
            if (retry_frames_ == 0 && !begin_rebuild())
                schedule_retry();
            break;
        case CatalogPhase::Enumerating:
            enumerate_batch();
            break;
        case CatalogPhase::Localizing:
            localize_batch();
            break;
        case CatalogPhase::WaitingForFactory:
        case CatalogPhase::Ready:
        case CatalogPhase::Failed:
            break;
        }
    }

    void CatalogRebuildJob::request_reload() noexcept
    {
        ta::adapters::eft::localization::EftLocalizationService::invalidate_language_cache();
        retry_frames_ = 0;
        retry_attempts_ = 0;
        reload_requested_ = true;
        if (factory_)
            phase_ = CatalogPhase::WaitingForFactory;
    }

    CatalogPhase CatalogRebuildJob::phase() const noexcept { return phase_; }
    bool CatalogRebuildJob::loading() const noexcept
    {
        return phase_ == CatalogPhase::Enumerating || phase_ == CatalogPhase::Localizing;
    }
    bool CatalogRebuildJob::ready() const noexcept { return phase_ == CatalogPhase::Ready; }
    std::uint64_t CatalogRebuildJob::revision() const noexcept { return revision_; }
    const std::vector<ta::features::items::contracts::ItemCatalogEntry>& CatalogRebuildJob::entries() const noexcept
    {
        return builder_.entries();
    }
    ta::features::items::contracts::ItemCategory CatalogRebuildJob::category_for(const std::string_view id) const
    {
        return builder_.category_for(id);
    }
    int CatalogRebuildJob::max_stack_for(const std::string_view id) const noexcept
    {
        return builder_.max_stack_for(id);
    }

    void CatalogRebuildJob::reset_for_factory(Il2CppObject* factory)
    {
        factory_ = factory;
        reader_.reset();
        builder_.reset(0);
        template_index_ = 0;
        localization_index_ = 0;
        retry_frames_ = 0;
        retry_attempts_ = 0;
        reload_requested_ = true;
        phase_ = CatalogPhase::WaitingForFactory;
        publish_revision();
    }

    bool CatalogRebuildJob::begin_rebuild()
    {
        if (!reader_.begin(factory_))
            return false;
        builder_.reset(reader_.count());
        template_index_ = 0;
        localization_index_ = 0;
        reload_requested_ = false;
        retry_attempts_ = 0;
        phase_ = CatalogPhase::Enumerating;
        publish_revision();
        return true;
    }

    void CatalogRebuildJob::enumerate_batch()
    {
        const std::size_t end = (std::min)(reader_.count(), template_index_ + kTemplateBatchSize);
        for (; template_index_ < end; ++template_index_)
        {
            if (auto record = reader_.read(template_index_))
                builder_.append(std::move(*record));
        }
        if (template_index_ < reader_.count())
            return;

        phase_ = CatalogPhase::Localizing;
        localization_index_ = 0;
        if (ta::foundation::diagnostics::build_options::verbose)
        {
            ta::foundation::logger::info(
                "ITEM", "runtime item catalog enumerated | entries=" +
                std::to_string(builder_.entries().size()));
        }
        if (builder_.entries().empty())
            localize_batch();
    }

    void CatalogRebuildJob::localize_batch()
    {
        if (!builder_.localize_batch(localization_index_, kLocalizationBatchSize))
            return;
        builder_.finalize();
        reader_.reset();
        phase_ = CatalogPhase::Ready;
        publish_revision();
    }

    void CatalogRebuildJob::schedule_retry()
    {
        ++retry_attempts_;
        if (retry_attempts_ >= (std::max)(1, retry_policy_.max_attempts))
        {
            phase_ = CatalogPhase::Failed;
            retry_frames_ = 0;
            reload_requested_ = false;
            publish_revision();
            return;
        }

        phase_ = CatalogPhase::RetryDelay;
        retry_frames_ = (std::max)(1, retry_policy_.delay_frames);
        reload_requested_ = true;
    }

    void CatalogRebuildJob::publish_revision() noexcept
    {
        ++revision_;
    }
}
