# frozen_string_literal: true

require "forwardable"

module Roadie
  # An asset provider that just composes a list of other asset providers.
  #
  # Give it a list of providers and they will all be tried in order.
  #
  # {ProviderList} behaves like an Array, *and* an asset provider, and can be coerced into an array.
  class ProviderList
    extend Forwardable
    include Enumerable

    # Wrap a single provider, or a list of providers into a {ProviderList}.
    #
    # @overload wrap(provider_list)
    #   @param [ProviderList] provider_list An actual instance of {ProviderList}.
    #   @return The passed in provider_list
    #
    # @overload wrap(provider)
    #   @param [asset provider] provider
    #   @return a new {ProviderList} with just the passed provider in it
    #
    # @overload wrap(provider1, provider2, ...)
    #   @return a new {ProviderList} with all the passed providers in it.
    def self.wrap(*providers)
      if providers.size == 1 && providers.first.instance_of?(self)
        providers.first
      else
        new(providers.flatten)
      end
    end

    # Returns a new empty list.
    def self.empty
      new([])
    end

    def initialize(providers)
      @providers = providers
    end

    # @return [Stylesheet, nil]
    def find_stylesheet(name)
      @providers.each do |provider|
        css = provider.find_stylesheet(name)
        return css if css
      end
      nil
    end

    # Tries to find the given stylesheet and raises an {ProvidersFailed} error
    # if no provider could find the asset.
    #
    # @return [Stylesheet]
    def find_stylesheet!(name)
      errors = []
      @providers.each do |provider|
        return provider.find_stylesheet!(name)
      rescue CssNotFound => error
        errors << error
      end
      raise ProvidersFailed.new(
        css_name: name, providers: self, errors: errors
      )
    end

    def to_s
      list = @providers.map { |provider|
        # Indent every line one level
        provider.to_s.split("\n").join("\n\t")
      }
      "ProviderList: [\n\t#{list.join(",\n\t")}\n]\n"
    end

    # ProviderList can be coerced to an array. This makes Array#flatten work
    # with it, among other things.
    def to_ary
      to_a
    end

    # @!method each
    #   @see Array#each
    # @!method size
    #   @see Array#size
    # @!method empty?
    #   @see Array#empty?
    # @!method push
    #   @see Array#push
    # @!method <<
    #   @see Array#<<
    # @!method pop
    #   @see Array#pop
    # @!method unshift
    #   @see Array#unshift
    # @!method shift
    #   @see Array#shift
    # @!method last
    #   @see Array#last
    def_delegators :@providers, :each, :size, :empty?, :push, :<<, :pop, :unshift, :shift, :last
  end
end
