class UserValidationService
  def initialize(controller)
    @controller = controller
  end

  extend Forwardable
  delegate %i[session initiate_wait_for_task session_init clear_current_user session_reset start_url_for_user url_for_only_path] => :@controller

  ValidateResult = Struct.new(:result, :flash_msg, :url)

  # Validate user login credentials
  #   return <url for redirect> as part of the result
  #
  def validate_user(user, task_id = nil, request = nil, authenticate_options = {})
    if task_id.present?
      validate_user_collect_task(user, task_id)
    else # First time thru, kick off authenticate task
      validation = validate_user_kick_off_task(user, request, authenticate_options)
      return validation unless validation.result == :pass
    end

    unless user[:name]
      clear_current_user
      return ValidateResult.new(:fail, @flash_msg ||= _("Error: Authentication failed"))
    end

    if user[:new_password].present?
      begin
        User.lookup_by_userid(user[:name]).change_password(user[:password], user[:new_password])
      rescue => bang
        return ValidateResult.new(:fail, "Error: " + bang.message)
      end
    end

    start_url = session[:start_url] # Hang on to the initial start URL
    db_user = User.lookup_by_userid(user[:name])
    session_reset
    feature = User.missing_user_features(db_user)
    if feature
      return ValidateResult.new(
        :fail,
        _("Login not allowed, User's %{feature} is missing. Please contact the administrator") % {:feature => feature}
      )
    end

    session_init(db_user)

    return validate_user_handle_not_ready(db_user) unless server_ready?

    # Start super admin at the main db if the main db has no records yet
    # If the main db has no records the default starting view is set to "Infrastructure Provider" view - the idea here is there is no point showing another view since it would be empty.
    # The above is true except Embedded Ansible provider which is added by default
    return validate_user_handle_no_records if db_user.super_admin_user? && no_records?

    startpage = start_url_for_user(start_url)
    unless startpage
      return ValidateResult.new(:fail, _("The user's role is not authorized for any access, please contact the administrator!"))
    end
    ValidateResult.new(:pass, nil, startpage)
  end

  private

  def no_records?
    if ::Settings.product.maindb
      ::Settings.product.maindb.constantize.count <= ::ManageIQ::Providers::EmbeddedAnsible::AutomationManager.count
    end
  end

  def validate_user_handle_no_records
    ValidateResult.new(:pass, nil, url_for_only_path(:controller => "ems_infra", :action => 'show_list'))
  end

  def validate_user_handle_not_ready(db_user)
    if db_user.super_admin_user?
      ValidateResult.new(:pass, nil, url_for_only_path(
                                       :controller    => "ops",
                                       :action        => 'explorer',
                                       :flash_warning => true,
                                       :no_refresh    => true,
                                       :flash_msg     => _("The %{product} Server is still starting, you have been redirected to the diagnostics page for problem determination") % {:product => Vmdb::Appliance.PRODUCT_NAME},
                                       :escape        => false
      ))
    else
      ValidateResult.new(:fail, _("The %{product} Server is still starting. If this message persists, please contact your %{product} administrator.") % {:product => Vmdb::Appliance.PRODUCT_NAME})
    end
  end

  def validate_user_kick_off_task(user, request, authenticate_options = {})
    validate_user_pre_auth_checks(user).tap { |result| return result if result }

    # Call the authentication, use wait_for_task if a task is spawned
    begin
      user_or_taskid = User.authenticate(user[:name], user[:password], request, authenticate_options)
    rescue MiqException::MiqEVMLoginError => err
      user[:name] = nil
      return ValidateResult.new(:fail, err.message)
    end

    if user_or_taskid.kind_of?(User)
      user[:name] = user_or_taskid.userid
      return ValidateResult.new(:pass)
    else
      initiate_wait_for_task(:task_id => user_or_taskid)
      return ValidateResult.new(:wait_for_task, nil)
    end
  end

  def validate_user_collect_task(user, task_id)
    task = MiqTask.find(task_id)
    if task.status.downcase != "ok"
      validate = ValidateResult.new(:fail, "Error: " + task.message)
      task.destroy
      return validate
    end
    user[:name] = task.userid
    task.destroy
    ValidateResult.new(:pass)
  end

  def validate_user_pre_auth_checks(user)
    # Pre_authenticate checks
    return ValidateResult.new(:fail, _("Error: Name is required")) if user.blank? || user[:name].blank?

    return ValidateResult.new(:fail, _("Error: New password and verify password must be the same")) if
      user[:new_password].present? && user[:new_password] != user[:verify_password]

    return ValidateResult.new(:fail, _("Error: New password can not be blank")) if user[:new_password] && user[:new_password].blank?

    return ValidateResult.new(:fail, _("Error: New password is the same as existing password")) if
      user[:new_password].present? && user[:password] == user[:new_password]
    nil
  end

  def server_ready?
    MiqServer.my_server(true).logon_status == :ready
  end
end
