# frozen_string_literal: true

require 'dry/struct'

module Twitch
  module Audit
    module Mixins
      # A mixin to allow for a lazy partial instantiation of a Dry::Struct
      module Partial

        class << self
          def extended(target)
            raise "#{name} can only be extended into subclasses of Dry::Struct" unless target <= Dry::Struct

            target.define_method(:partial?) { self.class.partial? }
          end
        end

        # Returns a new instance of of the partial subclass
        # @return [DryStructPartial]
        def partial
          partial_class.new
        end

        # Tests if this class is a partial subclass
        def partial?
          false
        end

        # Returns a memoized anonymous subclass of this class.
        # @return [Class DryStructPartial]
        def partial_class
          return self if partial?
          return @partial_class if instance_variable_defined?(:@partial_class)

          @partial_class = Class.new(self) do
            input Dry::Types['coercible.hash'].schema({})
            transform_types { |type| type.meta(omittable: true) }

            superclass.schema.each do |key, type|
              attribute key, type
              define_method("#{key}=") do |value|
                @attributes.update(key => value)
              end
            end

            define_singleton_method('partial?') { true }

            def resolved
              self.class.superclass.new(to_h)
            rescue StandardError => err
              puts "#{err.class} #{err.message}"
              raise err
            end

            def resolvable?
              resolved
              true
            rescue Dry::Struct::Error, Dry::Types::SchemaError, Dry::Types::MapError, Dry::Types::SchemaKeyError, Dry::Types::ConstraintError
              false
            end
          end

          Twitch::Audit.const_set("#{name.split('::').last}Partial", @partial_class)
          @partial_class
        end
      end
    end
  end
end
