module BCDD
  class Result
    VERSION: String
  end
end

class BCDD::Result
  private attr_accessor unknown: bool
  private attr_reader type_checker: BCDD::Result::Expectations::TypeChecker

  attr_reader data: BCDD::Result::Data
  attr_reader subject: untyped

  def initialize: (
    type: Symbol,
    value: untyped,
    ?subject: untyped,
    ?expectations: BCDD::Result::Expectations::Contract::Evaluator
  ) -> void

  def type: -> Symbol
  def value: -> untyped

  def success?: (?Symbol type) -> bool
  def failure?: (?Symbol type) -> bool

  def value_or: { () -> untyped } -> untyped

  def on: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
  def on_success: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
  def on_failure: (*Symbol) { (untyped, Symbol) -> void } -> BCDD::Result
  def on_unknown: () { (untyped, Symbol) -> void } -> BCDD::Result

  def and_then: (?Symbol method_name) { (untyped) -> untyped } -> BCDD::Result

  def handle: () { (BCDD::Result::Handler) -> void } -> untyped

  def ==: (untyped) -> bool
  def hash: -> Integer
  def inspect: -> String

  def deconstruct: -> [Symbol, [Symbol, untyped]]
  def deconstruct_keys: (Array[Symbol]) -> Hash[Symbol, Hash[Symbol, untyped]]

  alias eql? ==
  alias on_type on

  private

  def name: -> Symbol
  def known: (Proc) -> untyped
  def call_subject_method: (Symbol) -> BCDD::Result
  def ensure_result_object: (untyped, origin: Symbol) -> BCDD::Result
end

class BCDD::Result
  class Failure < BCDD::Result
  end

  def self.Success: (Symbol type, ?untyped value) -> BCDD::Result::Success
end

class BCDD::Result
  class Success < BCDD::Result
  end

  def self.Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
end

class BCDD::Result
  module Mixin
    def Success: (Symbol type, ?untyped value) -> BCDD::Result::Success

    def Failure: (Symbol type, ?untyped value) -> BCDD::Result::Failure
  end
end

class BCDD::Result
  class Data
    attr_reader name: Symbol
    attr_reader type: Symbol
    attr_reader value: untyped
    attr_reader to_h: Hash[Symbol, untyped]
    attr_reader to_a: [Symbol, Symbol, untyped]

    def initialize: (Symbol, Symbol, untyped) -> void

    def inspect: -> String

    alias to_ary to_a
    alias to_hash to_h
  end
end

class BCDD::Result
  class Error < ::StandardError
    def self.build: (**untyped) -> BCDD::Result::Error

    class NotImplemented < BCDD::Result::Error
    end

    class MissingTypeArgument < BCDD::Result::Error
    end

    class UnexpectedOutcome < BCDD::Result::Error
      def self.build: (outcome: untyped, origin: Symbol)
          -> BCDD::Result::Error::UnexpectedOutcome
    end

    class WrongResultSubject < BCDD::Result::Error
      def self.build: (given_result: BCDD::Result, expected_subject: untyped)
          -> BCDD::Result::Error::WrongResultSubject
    end

    class WrongSubjectMethodArity < BCDD::Result::Error
      def self.build: (subject: untyped, method: ::Method)
        -> BCDD::Result::Error::WrongSubjectMethodArity
    end

    class UnhandledTypes < BCDD::Result::Error
      def self.build: (types: Set[Symbol])
        -> BCDD::Result::Error::UnhandledTypes
    end
  end
end

class BCDD::Result
  class Handler
    UNDEFINED: Object

    def initialize: (
      BCDD::Result,
      type_checker: BCDD::Result::Expectations::TypeChecker
    ) -> void

    def []: (*Symbol) { (untyped, Symbol) -> void } -> untyped
    def failure: (*Symbol) { (untyped, Symbol) -> void } -> untyped
    def success: (*Symbol) { (untyped, Symbol) -> void } -> untyped
    def unknown: () { (untyped, Symbol) -> void } -> untyped

    alias type []

    private

    attr_reader result: BCDD::Result
    attr_reader allowed_types: BCDD::Result::Handler::AllowedTypes

    def outcome?: -> bool
    def outcome=: (Proc) -> void
    def outcome: -> untyped
  end
end

class BCDD::Result::Handler
  class AllowedTypes
    attr_reader unchecked: Set[Symbol]
    attr_reader type_checker: BCDD::Result::Expectations::TypeChecker

    def initialize: (
      BCDD::Result::Expectations::TypeChecker
    ) -> void

    def allow?: (Array[Symbol]) -> bool
    def allow_success?: (Array[Symbol]) -> bool
    def allow_failure?: (Array[Symbol]) -> bool

    def all_checked?: -> bool

    private

    def check!: (Array[Symbol], bool) -> bool
  end
end

class BCDD::Result::Expectations
  MIXIN_METHODS: String

  def self.mixin: (
    ?success: Hash[Symbol, untyped] | Array[Symbol],
    ?failure: Hash[Symbol, untyped] | Array[Symbol]
  ) -> Module

  def self.evaluate: (
    BCDD::Result::Data,
    BCDD::Result::Expectations::Contract::Evaluator
  ) -> BCDD::Result::Expectations::TypeChecker

  def initialize: (
    ?subject: untyped,
    ?success: Hash[Symbol, untyped] | Array[Symbol],
    ?failure: Hash[Symbol, untyped] | Array[Symbol],
    ?contract: BCDD::Result::Expectations::Contract::Evaluator
  ) -> void

  def Success: (Symbol, ?untyped) -> BCDD::Result::Success
  def Failure: (Symbol, ?untyped) -> BCDD::Result::Failure

  private

  attr_reader subject: untyped
  attr_reader contract: BCDD::Result::Expectations::Contract::Evaluator
end

module BCDD::Result::Expectations::Contract
  NONE: BCDD::Result::Expectations::Contract::Evaluator

  ToEnsure: ^(Hash[Symbol, untyped] | Array[Symbol])
    -> BCDD::Result::Expectations::Contract::Evaluator

  def self.new: (
    success: Hash[Symbol, untyped] | Array[Symbol],
    failure: Hash[Symbol, untyped] | Array[Symbol]
  ) -> BCDD::Result::Expectations::Contract::Evaluator
end

class BCDD::Result::Expectations
  class TypeChecker
    attr_reader result_type: Symbol
    attr_reader expectations: BCDD::Result::Expectations::Contract::Evaluator

    def initialize: (
      Symbol,
      expectations: BCDD::Result::Expectations::Contract::Evaluator
    ) -> void

    def allow?: (Array[Symbol]) -> bool
    def allow_success?: (Array[Symbol]) -> bool
    def allow_failure?: (Array[Symbol]) -> bool

    private

    def validate: (
      Array[Symbol],
      expected: BCDD::Result::Expectations::Contract::Interface,
      allow_empty: bool
    ) -> bool
  end
end

class BCDD::Result::Expectations::Error < BCDD::Result::Error
  class UnexpectedType < BCDD::Result::Expectations::Error
    def self.build: (type: Symbol, allowed_types: Set[Symbol])
        -> BCDD::Result::Expectations::Error::UnexpectedType
  end

  class UnexpectedValue < BCDD::Result::Expectations::Error
    def self.build: (type: Symbol, value: untyped)
      -> BCDD::Result::Expectations::Error::UnexpectedValue
  end
end

module BCDD::Result::Expectations::Contract
  module Interface
    def ==: (BCDD::Result::Expectations::Contract::Interface) -> bool

    def allowed_types: -> Set[Symbol]

    def type?: (Symbol) -> bool

    def type!: (Symbol) -> Symbol

    def type_and_value!: (BCDD::Result::Data) -> void

    def !=: (untyped) -> bool
  end
end

module BCDD::Result::Expectations::Contract
  module Disabled
    extend Interface

    EMPTY_SET: Set[Symbol]
  end
end

module BCDD::Result::Expectations::Contract
  class ForTypes
    include Interface

    def initialize: (Array[Symbol]) -> void
  end
end

module BCDD::Result::Expectations::Contract
  class ForTypesAndValues
    include Interface

    def initialize: (Hash[Symbol, untyped]) -> void
  end
end

module BCDD::Result::Expectations::Contract
  class Evaluator
    include Interface

    attr_reader allowed_types: Set[Symbol]
    attr_reader success: BCDD::Result::Expectations::Contract::Interface
    attr_reader failure: BCDD::Result::Expectations::Contract::Interface

    def initialize: (
      BCDD::Result::Expectations::Contract::Interface,
      BCDD::Result::Expectations::Contract::Interface
    ) -> void

    private

    def for: (BCDD::Result::Data) -> BCDD::Result::Expectations::Contract::Interface
  end
end