#pragma once

#include <string>
#include <optional>
#include <unordered_map>
#include "../ast/types.hpp"
#include "../../utils/string.hpp"
#include "../../protocol/protocol.hpp"

namespace lsp::language::symbol
{
    using SymbolKind = protocol::SymbolKind;
    enum class ScopeKind
    {
        Global,
        Unit,
        Class,
        Function,
        Block
    };

    // 符号ID类型
    using SymbolId = uint64_t;
    constexpr SymbolId kInvalidSymbolId = 0;

    using ScopeId = uint64_t;
    constexpr ScopeId kInvalidScopeId = 0;

    struct UnitImport
    {
        std::string unit_name;
        ast::Location location;
    };

    struct SymbolDefinition
    {
        SymbolId id = kInvalidSymbolId;
        std::string name;
        SymbolKind kind;
        ast::Location location;
        ast::Location selection_range;

        std::optional<std::string> type_hint;

        std::string detail;

        std::optional<ast::AccessModifier> access_modifier;
        std::optional<ast::MethodModifier> method_modifier;
        std::optional<ast::ReferenceModifier> reference_modifier;
        bool is_class_method = false;

        std::optional<SymbolId> parent_id;
    };

    struct ScopeInfo
    {
        ScopeId scope_id;
        ScopeKind kind;
        ast::Location range;
        std::optional<ScopeId> parent_scope_id;
        std::optional<SymbolId> associated_symbol_id;
        std::unordered_map<std::string, SymbolId, utils::IHasher, utils::IEqualTo> symbols;
    };

    struct Reference
    {
        ast::Location location;
        SymbolId symbol_id;
        bool is_definition;
        bool is_write;
    };

    struct CallRelation
    {
        SymbolId caller;
        SymbolId callee;
        ast::Location call_location;
    };

} // namespace lsp::language::symbol
