#pragma once

#include <string>
#include <vector>
#include <cstdint>
#include "core/memory.hpp"
#include "sdk/datatypes/utl_ts_hash.hpp"

class c_schema_class_field_data
{
public:
    SCHEMA_ADD_STRING_OFFSET(m_name, 0x00);
    SCHEMA_ADD_OFFSET(uint16_t, m_single_inheritance_offset, 0x10);
    uint8_t m_pad0[0x20];
};
static_assert(sizeof(c_schema_class_field_data) == 0x20, "wrong size on c_schema_class_field_data");

class c_schema_type_declared_class
{
public:
    SCHEMA_ADD_STRING_OFFSET(m_binary_name, 0x08);

    std::pair<uint16_t, c_schema_class_field_data*> get_fields() const
    {
        return
        {
            m_memory->read_t<uint16_t>(reinterpret_cast<uintptr_t>(this) + 0x24),
            m_memory->read_t<c_schema_class_field_data*>(reinterpret_cast<uintptr_t>(this) + 0x30)
        };
    }
};

class c_schema_system_type_scope
{
public:
    std::string m_module_name() const
    {
        // The module name is stored inline as a C-string at offset 0x08,
        // NOT as a pointer. Read 64 chars directly (SSO-safe).
        return m_memory->read_string(reinterpret_cast<uintptr_t>(this) + 0x08);
    }

    c_utl_ts_hash<c_schema_type_declared_class*, 256, uint32_t> m_hash_classes() const
    {
        return m_memory->read_t<c_utl_ts_hash<c_schema_type_declared_class*, 256, uint32_t>>(reinterpret_cast<uintptr_t>(this) + 0x540);
    }
};

class c_schema_system
{
public:
    class c_schema_system_type_scope* find_type_scope_for_module(const std::string_view& name) const
    {
        const auto size = m_memory->read_t<uint32_t>(reinterpret_cast<uintptr_t>(this) + 0x190);
        LOG_INFO("type_scope size = %u", size);
        if (!size || size >= 50)
        {
            LOG_ERROR("type scope size is either empty or not good (value=%u)", size);
            return {};
        }

        const auto data = m_memory->read_t<uintptr_t>(reinterpret_cast<uintptr_t>(this) + 0x198);
        LOG_INFO("type_scope data ptr = %p", (void*)data);
        if (!data || data <= 0x10000)
        {
            LOG_ERROR("type scope data is either empty or not good (ptr=%p)", (void*)data);
            return {};
        }

        std::vector<c_schema_system_type_scope*> type_scopes{};
        type_scopes.resize(size);

        if (!m_memory->read_t(data, type_scopes.data(), size * sizeof(uintptr_t)))
        {
            LOG_ERROR("failed to read type scope array at %p (size=%u)", (void*)data, size);
            return {};
        }

        for (uint32_t i = 0; i < size; i++)
        {
            const auto& type_scope = type_scopes[i];
            if (!type_scope)
            {
                LOG_WARNING("type_scope[%u] is null, skipping", i);
                continue;
            }

            const auto module_name = type_scope->m_module_name();
            LOG_INFO("type_scope[%u] module_name='%s' (empty=%d)", i, module_name.c_str(), module_name.empty());
            if (module_name.empty() || module_name.find(name) == std::string::npos)
                continue;

            LOG_INFO("FOUND type_scope for '%s' at %p", name.data(), (void*)type_scope);
            return type_scope;
        }

        LOG_ERROR("no type_scope found for module '%s' (%u scopes checked)", name.data(), size);
        return {};
    }
};
