module RBS
  module AST
    module Declarations
      type t = Class | Module | Interface | Constant | Global | TypeAlias | ClassAlias | ModuleAlias

      class Base
      end

      interface _WithMember
        def members: () -> Array[untyped]
      end

      module NestedDeclarationHelper : _WithMember
        def each_member: () { (Members::t) -> void } -> void
                       | () -> Enumerator[Members::t, void]

        def each_decl: () { (t) -> void } -> void
                     | () -> Enumerator[t, void]
      end

      module MixinHelper : _WithMember
        type mixin_member = Members::Include | Members::Extend | Members::Prepend

        @mixins: Array[mixin_member]

        def each_mixin: () { (mixin_member) -> void } -> void
                      | () -> Enumerator[mixin_member, void]
      end

      class Class < Base
        class Super
          # String
          # ^^^^^^  name
          #
          # Array[String]
          # ^^^^^         name
          #      ^^^^^^^^ args
          #
          type loc = Location[:name, :args]

          attr_reader name: TypeName
          attr_reader args: Array[Types::t]
          attr_reader location: loc?

          def initialize: (name: TypeName, args: Array[Types::t], location: loc?) -> void

          include _HashEqual
          include _ToJson
        end

        type member = Members::t | t

        # class Foo end
        # ^^^^^         keyword
        #       ^^^     name
        #           ^^^ end
        #
        # class Foo[A] < String end
        # ^^^^^                     keyword
        #       ^^^                 name
        #          ^^^              type_params
        #              ^            lt
        #                       ^^^ end
        #
        type loc = Location[:keyword | :name | :end, :type_params | :lt]

        include NestedDeclarationHelper
        include MixinHelper

        attr_reader name: TypeName
        attr_reader type_params: Array[TypeParam]
        attr_reader members: Array[member]
        attr_reader super_class: Super?
        attr_reader annotations: Array[Annotation]
        attr_reader location: loc?
        attr_reader comment: Comment?

        def initialize: (name: TypeName, type_params: Array[TypeParam], members: Array[member], super_class: Super?, annotations: Array[Annotation], location: loc?, comment: Comment?) -> void

        def update: (?name: TypeName, ?type_params: Array[TypeParam], ?members: Array[member], ?super_class: Super?, ?annotations: Array[Annotation], ?location: loc?, ?comment: Comment?) -> Declarations::Class

        include _HashEqual
        include _ToJson
      end

      class Module < Base
        class Self
          # _Each[String]
          # ^^^^^         name
          #      ^^^^^^^^ args
          #
          type loc = Location[:name, :args]

          attr_reader name: TypeName
          attr_reader args: Array[Types::t]
          attr_reader location: loc?

          def initialize: (name: TypeName, args: Array[Types::t], location: loc?) -> void

          include _HashEqual
          include _ToJson

          def to_s: () -> String
        end

        type member = Members::t | t

        # module Foo end
        # ^^^^^^         keyword
        #        ^^^     name
        #            ^^^ end
        #
        # module Foo[A] : BasicObject end
        # ^^^^^^                          keyword
        #        ^^^                      name
        #           ^^^                   type_params
        #               ^                 colon
        #                 ^^^^^^^^^^^     self_types
        #                             ^^^ end
        #
        type loc = Location[:keyword | :name | :end, :type_params | :colon | :self_types]

        include NestedDeclarationHelper
        include MixinHelper

        attr_reader name: TypeName
        attr_reader type_params: Array[TypeParam]
        attr_reader members: Array[member]
        attr_reader location: loc?
        attr_reader annotations: Array[Annotation]
        attr_reader self_types: Array[Self]
        attr_reader comment: Comment?

        def initialize: (name: TypeName, type_params: Array[TypeParam], members: Array[member], location: loc?, annotations: Array[Annotation], self_types: Array[Self], comment: Comment?) -> void

        def update: (?name: TypeName, ?type_params: Array[TypeParam], ?members: Array[member], ?location: loc?, ?annotations: Array[Annotation], ?self_types: Array[Self], ?comment: Comment?) -> Declarations::Module

        include _HashEqual
        include _ToJson
      end

      class Interface < Base
        type member = Members::t

        # interface _Foo end
        # ^^^^^^^^^          keyword
        #           ^^^^     name
        #                ^^^ end
        #
        # interface _Bar[A, B] end
        # ^^^^^^^^^                keyword
        #           ^^^^           name
        #               ^^^^^^     type_params
        #                      ^^^ end
        #
        type loc = Location[:name | :keyword | :end, :type_params]

        attr_reader name: TypeName
        attr_reader type_params: Array[TypeParam]
        attr_reader members: Array[member]
        attr_reader annotations: Array[Annotation]
        attr_reader location: loc?
        attr_reader comment: Comment?

        def initialize: (name: TypeName, type_params: Array[TypeParam], members: Array[member], annotations: Array[Annotation], location: loc?, comment: Comment?) -> void

        def update: (?name: TypeName, ?type_params: Array[TypeParam], ?members: Array[member], ?annotations: Array[Annotation], ?location: loc?, ?comment: Comment?) -> Declarations::Interface

        include MixinHelper

        include _HashEqual
        include _ToJson
      end

      class TypeAlias < Base
        # type loc[T] = Location[T, bot]
        # ^^^^                            keyword
        #      ^^^                        name
        #         ^^^                     type_params
        #             ^                      eq
        #
        type loc = Location[:keyword | :name | :eq, :type_params]

        attr_reader name: TypeName
        attr_reader type_params: Array[TypeParam]
        attr_reader type: Types::t
        attr_reader annotations: Array[Annotation]
        attr_reader location: loc?
        attr_reader comment: Comment?

        def initialize: (name: TypeName, type_params: Array[TypeParam], type: Types::t, annotations: Array[Annotation], location: loc?, comment: Comment?) -> void

        include _HashEqual
        include _ToJson
      end

      class Constant < Base
        # VERSION: String
        # ^^^^^^^         name
        #        ^        colon
        #
        type loc = Location[:name | :colon, bot]

        attr_reader name: TypeName
        attr_reader type: Types::t
        attr_reader location: loc?
        attr_reader comment: Comment?
        attr_reader annotations: Array[Annotation]

        def initialize: (name: TypeName, type: Types::t, location: loc?, comment: Comment?, annotations: Array[Annotation]) -> void
                      | %a{deprecated} (name: TypeName, type: Types::t, location: loc?, comment: Comment?) -> void

        include _HashEqual
        include _ToJson
      end

      class Global < Base
        # $SIZE: String
        # ^^^^^         name
        #      ^        colon
        #
        type loc = Location[:name | :colon, bot]

        attr_reader name: Symbol
        attr_reader type: Types::t
        attr_reader location: loc?
        attr_reader comment: Comment?
        attr_reader annotations: Array[Annotation]

        def initialize: (name: Symbol, type: Types::t, location: loc?, comment: Comment?, annotations: Array[Annotation]) -> void
                      | %a{deprecated} (name: Symbol, type: Types::t, location: loc?, comment: Comment?) -> void

        include _HashEqual
        include _ToJson
      end

      class AliasDecl < Base
        # module Foo = Bar
        # ^^^^^^             keyword
        #        ^^^         new_name
        #            ^       eq
        #              ^^^   old_name
        #
        # class Foo = Bar
        # ^^^^^              keyword
        #
        type loc = Location[:keyword | :new_name | :eq | :old_name, bot]

        attr_reader new_name: TypeName

        attr_reader old_name: TypeName

        attr_reader location: loc?

        attr_reader comment: Comment?

        attr_reader annotations: Array[Annotation]

        def initialize: (new_name: TypeName, old_name: TypeName, location: loc?, comment: Comment?, annotations: Array[Annotation]) -> void
                      | %a{deprecated} (new_name: TypeName, old_name: TypeName, location: loc?, comment: Comment?) -> void

        include _HashEqual
      end

      class ClassAlias < AliasDecl
        include _ToJson
      end

      class ModuleAlias < AliasDecl
        include _ToJson
      end
    end
  end
end
