class Steep::Project

def clear

def clear
  listener.clear_project project: self do
    @signature = nil
    source_files.each_value do |file|
      file.invalidate
    end
  end
end

def each_updated_source(force: false)

@type method each_updated_source: (?force: bool) ?{ (SourceFile) -> any } -> any
def each_updated_source(force: false)
  if block_given?
    source_files.each_value do |file|
      if force || file.requires_type_check?
        yield file
      end
    end
  else
    enum_for :each_updated_source, force: force
  end
end

def errors

def errors
  source_files.flat_map do |_, file|
    file.errors || []
  end
end

def has_type_error?

def has_type_error?
  source_files.any? do |_, file|
    file.errors&.any?
  end
end

def initialize(listener = nil)

def initialize(listener = nil)
  @listener = listener || NullListener.new
  @source_files = {}
  @signature_files = {}
end

def reload_signature

def reload_signature
  @signature = nil
  env = AST::Signature::Env.new
  builder = Interface::Builder.new(signatures: env)
  check = Subtyping::Check.new(builder: builder)
  # @type var syntax_errors: Hash<Pathname, any>
  syntax_errors = {}
  listener.load_signature(project: self) do
    signature_files.each_value do |file|
      sigs = listener.parse_signature(project: self, file: file) do
        file.parse
      end
      sigs.each do |sig|
        env.add sig
      end
    rescue Racc::ParseError => exn
      Steep.logger.warn { "Syntax error on #{file.path}: #{exn.inspect}" }
      syntax_errors[file.path] = exn
    end
    if syntax_errors.empty?
      listener.validate_signature(project: self) do
        errors = validate_signature(check)
        @signature = if errors.empty?
                       SignatureLoaded.new(check: check, loaded_at: Time.now, file_paths: signature_files.keys)
                     else
                       SignatureHasError.new(errors: errors)
                     end
      end
    else
      @signature = SignatureHasSyntaxError.new(errors: syntax_errors)
    end
  end
end

def signature_updated?

def signature_updated?
  case sig = signature
  when SignatureLoaded
    signature_files.keys != sig.file_paths ||
      signature_files.any? {|_, file| file.content_updated_at >= sig.loaded_at }
  else
    true
  end
end

def success?

def success?
  signature.is_a?(SignatureLoaded) &&
    source_files.all? {|_, file| file.source.is_a?(Source) && file.typing }
end

def type_check(force_signatures: false, force_sources: false)

def type_check(force_signatures: false, force_sources: false)
  listener.check(project: self) do
    should_reload_signature = force_signatures || signature_updated?
    reload_signature if should_reload_signature
    case sig = signature
    when SignatureLoaded
      each_updated_source(force: force_sources || should_reload_signature) do |file|
        file.invalidate
        listener.parse_source(project: self, file: file) do
          file.parse()
        end
        listener.type_check_source(project: self, file: file) do
          file.type_check(sig.check)
        end
      end
    end
  end
end

def validate_signature(check)

def validate_signature(check)
  errors = []
  builder = check.builder
  check.builder.signatures.each do |sig|
    Steep.logger.debug { "Validating signature: #{sig.inspect}" }
    case sig
    when AST::Signature::Interface
      yield_self do
        instance_interface = builder.build_interface(sig.name)
        args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
        instance_type = AST::Types::Name::Interface.new(name: sig.name, args: args)
        instance_interface.instantiate(type: instance_type,
                                       args: args,
                                       instance_type: instance_type,
                                       module_type: nil).validate(check)
      end
    when AST::Signature::Module
      yield_self do
        instance_interface = builder.build_instance(sig.name)
        instance_args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
        module_interface = builder.build_module(sig.name)
        module_args = module_interface.params.map {|var| AST::Types::Var.fresh(var) }
        instance_type = AST::Types::Name::Instance.new(name: sig.name, args: instance_args)
        module_type = AST::Types::Name::Module.new(name: sig.name)
        Steep.logger.debug { "Validating instance methods..." }
        instance_interface.instantiate(type: instance_type,
                                       args: instance_args,
                                       instance_type: instance_type,
                                       module_type: module_type).validate(check)
        Steep.logger.debug { "Validating class methods..." }
        module_interface.instantiate(type: module_type,
                                     args: module_args,
                                     instance_type: instance_type,
                                     module_type: module_type).validate(check)
      end
    when AST::Signature::Class
      yield_self do
        instance_interface = builder.build_instance(sig.name)
        instance_args = instance_interface.params.map {|var| AST::Types::Var.fresh(var) }
        module_interface = builder.build_class(sig.name, constructor: true)
        module_args = module_interface.params.map {|var| AST::Types::Var.fresh(var) }
        instance_type = AST::Types::Name::Instance.new(name: sig.name, args: instance_args)
        module_type = AST::Types::Name::Class.new(name: sig.name, constructor: true)
        Steep.logger.debug { "Validating instance methods..." }
        instance_interface.instantiate(type: instance_type,
                                       args: instance_args,
                                       instance_type: instance_type,
                                       module_type: module_type).validate(check)
        Steep.logger.debug { "Validating class methods..." }
        module_interface.instantiate(type: module_type,
                                     args: module_args,
                                     instance_type: instance_type,
                                     module_type: module_type).validate(check)
      end
    end
  rescue => exn
    errors << exn
  end
  errors
end