require 'morphine'

require_relative_all %w(concerns .), 'services'

module Locomotive
  module Steam

    module Services

      def self.build_instance(request = nil)
        Instance.new(request).tap do |instance|
          if Locomotive::Steam.configuration.services_hook
            Locomotive::Steam.configuration.services_hook.call(instance)
          end
        end
      end

      # Used to get an easy access to some of the services (url_builder)
      # without passing a request
      def self.build_simple_instance(site)
        Instance.new(nil).tap do |instance|
          instance.current_site = site
        end
      end

      class Defer < SimpleDelegator
        def initialize(&block)
          @constructor = block
          super(nil)
        end
        def __getobj__
          super || __setobj__(@constructor.call)
        end
        def nil?
          __getobj__.nil?
        end
      end

      class Instance

        include Morphine

        attr_accessor_initialize :request

        register :current_site do
          repositories.current_site = Defer.new { site_finder.find }
        end

        register :repositories do
          Steam::Repositories.new(nil, nil, configuration)
        end

        register :site_finder do
          Steam::SiteFinderService.new(repositories.site, request)
        end

        register :page_finder do
          Steam::PageFinderService.new(repositories.page)
        end

        register :parent_finder do
          Steam::ParentFinderService.new(repositories.page)
        end

        register :editable_element do
          Steam::EditableElementService.new(repositories.page, locale)
        end

        register :snippet_finder do
          Steam::SnippetFinderService.new(repositories.snippet)
        end

        register :section_finder do
          Steam::SectionFinderService.new(repositories.section)
        end

        register :action do
          Steam::ActionService.new(current_site, email, content_entry: content_entry, api: external_api, redirection: page_redirection, cookie: cookie)
        end

        register :content_entry do
          Steam::ContentEntryService.new(repositories.content_type, repositories.content_entry, locale)
        end

        register :entry_submission do
          Steam::EntrySubmissionService.new(content_entry)
        end

        register :liquid_parser do
          Steam::LiquidParserService.new(parent_finder, snippet_finder)
        end

        register :url_builder do
          Steam::UrlBuilderService.new(current_site, locale, request&.env&.fetch('steam.mounted_on', nil))
        end

        register :url_finder do
          Steam::UrlFinderService.new(url_builder, page_finder, content_entry)
        end

        register :page_redirection do
          Steam::PageRedirectionService.new(page_finder, url_builder)
        end

        register :theme_asset_url do
          Steam::ThemeAssetUrlService.new(repositories.theme_asset, asset_host, configuration.theme_assets_checksum)
        end

        register :asset_host do
          Steam::AssetHostService.new(request, current_site, configuration.asset_host)
        end

        register :image_resizer do
          Steam::ImageResizerService.new(::Dragonfly.app(:steam), configuration.asset_path)
        end

        register :translator do
          Steam::TranslatorService.new(repositories.translation, locale)
        end

        register :external_api do
          Steam::ExternalAPIService.new
        end

        register :csrf_protection do
          Steam::CsrfProtectionService.new(configuration.csrf_protection, Rack::Csrf.field, Rack::Csrf.token(request.env))
        end

        register :markdown do
          Steam::MarkdownService.new
        end

        register :textile do
          Steam::TextileService.new
        end

        register :email do
          Steam::EmailService.new(page_finder, liquid_parser, asset_host, configuration.mode == :test)
        end

        register :auth do
          Steam::AuthService.new(current_site, content_entry, email)
        end

        register :recaptcha do
          Steam::RecaptchaService.new(current_site, request)
        end

        register :cache do
          Steam::NoCacheService.new
        end

        register :cookie do
          Steam::CookieService.new(request)
        end

        register :configuration do
          Locomotive::Steam.configuration
        end

        register :locale do
          I18n.locale
        end

        def locale
          @locale || I18n.locale
        end

        def locale=(locale)
          # Note: "repositories" has already been initialized when called here.
          @locale = repositories.locale = locale
        end

        def set_site(site)
          self.current_site.__setobj__(site)
        end

        def defer(name, &block)
          send(:"#{name}=", Defer.new(&block))
        end

      end

    end

  end
end
