# frozen_string_literal: true

require_relative 'attribute'

module LedgerSync
  module Serialization
    class DeserializerAttribute < Attribute
      def initialize(args = {})
        super

        raise 'Missing resource_attribute' if resource_attribute.blank?

        if block.present?
          raise 'block and hash_attribute cannot both be present' if hash_attribute.present?
        else
          @hash_attribute ||= resource_attribute
        end
      end

      def references_many?
        type.is_a?(Type::DeserializerReferencesManyType)
      end

      def references_one?
        type.is_a?(Type::DeserializerReferencesOneType)
      end

      def value_from_hash(hash:, resource:)
        return block_value_for(hash: hash) if block.present?

        value = hash.dig(*hash_attribute.split('.'))

        if type.is_a?(Type::DeserializerType)
          type.cast(deserializer_attribute: self, resource: resource, value: value)
        else
          value = type.cast(value: value)
          return value if resource_attribute_dot_parts.count <= 1

          nested_resource = resource.send(resource_attribute_dot_parts.first)
          nested_resource ||= resource_attribute_class(resource: resource).new

          build_resource_value_from_nested_attributes(
            nested_resource,
            value,
            resource_attribute_dot_parts[1..-1]
          )
        end
      end

      private

      def build_resource_value_from_nested_attributes(resource, value, attribute_parts)
        resource = resource.dup
        first_attribute, *remaining_attributes = attribute_parts

        if remaining_attributes.none?
          resource.public_send("#{first_attribute}=", value)
        else
          resource.public_send(
            "#{first_attribute}=",
            build_resource_value_from_nested_attributes(
              resource.public_send(
                first_attribute
              ) || resource.class.resource_attributes[first_attribute.to_sym].type.resource_class.new,
              value,
              remaining_attributes
            )
          )
        end

        resource
      end

      def resource_attribute_class(resource:)
        @resource_attribute_type ||= {}
        @resource_attribute_type[resource] ||= resource.class.resource_attributes[
          resource_attribute_dot_parts.first.to_sym
        ].type.resource_class
      end
    end
  end
end
