#include "./scope.hpp"

namespace lsp::language::symbol
{
    ScopeId ScopeManager::CreateScope(ScopeKind kind, const ast::Location& range, std::optional<ScopeId> parent_scope_id, std::optional<SymbolId> associated_symbol_id)
    {
        ScopeId new_scope_id = next_scope_id_++;

        ScopeInfo info;
        info.scope_id = new_scope_id;
        info.kind = kind;
        info.range = range;
        info.parent_scope_id = parent_scope_id;
        info.associated_symbol_id = associated_symbol_id;

        scopes_[new_scope_id] = std::move(info);

        if (kind == ScopeKind::Global)
            global_scope_id_ = new_scope_id;

        return new_scope_id;
    }

    void ScopeManager::AddSymbol(ScopeId scope_id, const std::string& name, SymbolId symbol_id)
    {
        auto it = scopes_.find(scope_id);
        if (it != scopes_.end())
            it->second.symbols[name] = symbol_id;
    }

    std::optional<SymbolId> ScopeManager::FindInScope(ScopeId scope_id, const std::string& name) const
    {
        auto it = scopes_.find(scope_id);
        if (it == scopes_.end())
            return std::nullopt;

        auto sym_it = it->second.symbols.find(name);
        if (sym_it != it->second.symbols.end())
            return sym_it->second;

        return std::nullopt;
    }

    std::optional<SymbolId> ScopeManager::FindInScopeChain(ScopeId scope_id, const std::string& name) const
    {
        auto current_id = scope_id;

        while (current_id != kInvalidScopeId)
        {
            if (auto result = FindInScope(current_id, name))
                return result;

            auto it = scopes_.find(current_id);
            if (it == scopes_.end() || !it->second.parent_scope_id)
                break;

            current_id = *it->second.parent_scope_id;
        }

        return std::nullopt;
    }

    const ScopeInfo* ScopeManager::GetScopeInfo(ScopeId scope_id) const
    {
        auto it = scopes_.find(scope_id);
        return it != scopes_.end() ? &it->second : nullptr;
    }

    std::vector<SymbolId> ScopeManager::GetSymbolsInScope(ScopeId scope_id) const
    {
        std::vector<SymbolId> result;
        auto it = scopes_.find(scope_id);
        if (it != scopes_.end())
        {
            for (const auto& [name, id] : it->second.symbols)
                result.push_back(id);
        }
        return result;
    }

    void ScopeManager::Clear()
    {
        scopes_.clear();
        global_scope_id_ = kInvalidScopeId;
        next_scope_id_ = 1;
    }

}
