# typed: strict
module HasCompositeToParam
  extend T::Sig
  extend T::Helpers
  abstract!
  requires_ancestor { Kernel }
  requires_ancestor { ReduxApplicationRecord }

  sig(:final) { returns(T.nilable(String)) }
  def to_param
    klass, param = T.unsafe(self.class).param_prefix_and_attribute
    param_value = send(param)
    param_value.present? ? "#{klass}@#{param_value}" : nil
  end

  module ClassMethods
    extend T::Sig
    extend T::Helpers
    extend T::Generic
    requires_ancestor { Kernel }
    has_attached_class!
    abstract!

    sig(:final) do
      params(param: T.nilable(String)).returns(T.nilable([String, String]))
    end
    def parse_composite_param(param)
      return nil unless param.present?
      klass, param_value = param.split("@", 2)
      return nil unless klass.present? && param_value.present?
      [klass, param_value]
    end

    sig(:final) do
      params(param: T.nilable(String)).returns(T.nilable(T.attached_class))
    end
    def find_by_param(param)
      composite_param = parse_composite_param(param)
      if composite_param.nil?
        raise ActionController::BadRequest, "invalid id: #{param.inspect}"
      end

      klass_param, id = composite_param
      klass = self
      group_klass =
        T
          .unsafe(klass)
          .subclasses
          .find do |subclass|
            param_prefix, _ = subclass.param_prefix_and_attribute
            param_prefix == klass_param
          end

      if group_klass.nil?
        raise ActionController::BadRequest, "unknown model type: #{klass_param}"
      end
      _, param_attribute = group_klass.param_prefix_and_attribute
      group_klass.find_by(param_attribute => id)
    end

    sig { abstract.returns([String, Symbol]) }
    def param_prefix_and_attribute
    end

    sig(:final) { returns(String) }
    def param_prefix
      param_prefix_and_attribute[0]
    end

    sig(:final) { returns(Symbol) }
    def param_attribute
      param_prefix_and_attribute[1]
    end

    sig { overridable.returns(Symbol) }
    def param_order_attribute
      param_attribute
    end
  end

  mixes_in_class_methods(ClassMethods)
end
