#pragma once

#include <cstdint>
#include <filesystem>

struct Il2CppObject;

namespace ta::adapters::unity::chams
{
    class ChamsBundleRuntime final
    {
    public:
        static ChamsBundleRuntime& instance() noexcept;

        [[nodiscard]] bool initialize(std::filesystem::path bundle_path) noexcept;
        void tick();
        void reset() noexcept;

        [[nodiscard]] Il2CppObject* current_bundle() const noexcept;
        [[nodiscard]] Il2CppObject* load_asset(const char* name, Il2CppObject* system_type) const;

    private:
        enum class LoadFailure : std::uint8_t
        {
            None,
            AssetBundleImageUnavailable,
            CoreImageUnavailable,
            ClassUnavailable,
            MethodUnavailable,
            StringAllocationFailed,
            RequestCreationFailed,
            CompletionMethodUnavailable,
            AssetBundleAccessorUnavailable,
            UnityReturnedNull
        };

        enum class PollResult : std::uint8_t
        {
            Pending,
            Loaded,
            Failed
        };

        std::filesystem::path bundle_path_;
        Il2CppObject* request_ = nullptr;
        Il2CppObject* bundle_ = nullptr;
        std::uint32_t load_attempts_ = 0;
        std::uint32_t retry_frames_ = 0;
        int last_progress_bucket_ = -1;
        bool initialized_ = false;

        [[nodiscard]] Il2CppObject* start_async_load(LoadFailure& failure);
        [[nodiscard]] PollResult poll_async_load(LoadFailure& failure);
        void schedule_retry(LoadFailure failure);
        [[nodiscard]] static const char* failure_name(LoadFailure failure) noexcept;
    };
}
