class ServiceTemplateProvisionTask < MiqRequestTask
  validate :validate_request_type, :validate_state

  virtual_belongs_to :service_resource, :class_name => "ServiceResource"

  AUTOMATE_DRIVES = true

  def self.base_model
    ServiceTemplateProvisionTask
  end

  def statemachine_task_status
    %w[finished provisioned].include?(state) ? status.to_s.downcase : "retry"
  end

  def my_zone
    dialog_zone || source.my_zone || MiqServer.my_zone
  end

  def provision_priority
    return 0 if service_resource.nil?

    service_resource.provision_index
  end

  def sibling_sequence_run_now?
    return true  if miq_request_task.nil? || miq_request_task.miq_request_tasks.count == 1
    return false if miq_request_task.miq_request_tasks.detect { |t| t.provision_priority < provision_priority && t.state != "finished" }

    true
  end

  def group_sequence_run_now?
    parent = miq_request_task
    return true   if parent.nil?
    return false  unless parent.group_sequence_run_now?
    return false  unless sibling_sequence_run_now?

    true
  end

  def self.get_description(req_obj)
    prov_source = req_obj.source
    svc_target_name = req_obj.get_option(:target_name)
    if svc_target_name.blank?
      svc_target_name = prov_source.respond_to?(:get_source_name) ? prov_source.get_source_name : prov_source.name
    end

    if req_obj.kind_of?(ServiceTemplateProvisionRequest)
      result = _("Provisioning Service [%{svc_target_name}] from [%{prov_source_name}]") % {:svc_target_name => svc_target_name, :prov_source_name => prov_source.name}
    else
      service_resource = prov_source
      rsc_name = service_resource.name if service_resource.respond_to?(:name)
      if rsc_name.blank?
        req_template = service_resource.resource
        rsc_name = req_template.respond_to?(:get_source_name) ? req_template.get_source_name : req_template.name
      end
      if svc_target_name.blank?
        svc_tmp_id = req_obj.get_option(:src_id)
        svc_tmp = ServiceTemplate.find_by(:id => svc_tmp_id)
        svc_target_name = svc_tmp.name
      end

      result = case req_template
               when ServiceTemplate
                 _("Provisioning Service [%{rsc_name}] for Service [%{svc_target_name}]") % {:rsc_name => rsc_name, :svc_target_name => svc_target_name}
               when MiqProvisionRequestTemplate
                 _("Provisioning VM [%{rsc_name}] for Service [%{svc_target_name}]") % {:rsc_name => rsc_name, :svc_target_name => svc_target_name}
               else
                 _("Provisioning [%{rsc_name}] for Service [%{svc_target_name}]") % {:rsc_name => rsc_name, :svc_target_name => svc_target_name}
               end
    end

    result
  end

  def after_request_task_create
    update_attribute(:description, get_description)
    create_child_tasks
  end

  def create_child_tasks
    parent_svc = Service.find_by(:id => options[:parent_service_id])
    parent_name = parent_svc.nil? ? 'none' : "#{parent_svc.class.name}:#{parent_svc.id}"
    _log.info("- creating service tasks for service <#{self.class.name}:#{id}> with parent service <#{parent_name}>")

    tasks = source.create_tasks_for_service(self, parent_svc)
    tasks.each { |t| miq_request_tasks << t }
    _log.info("- created <#{tasks.length}> service tasks for service <#{self.class.name}:#{id}> with parent service <#{parent_name}>")
  end

  def do_request
    if miq_request_tasks.size.zero?
      message = "Service does not have children processes"
      _log.info(message)
      update_and_notify_parent(:state => 'provisioned', :message => message)
    else
      miq_request_tasks.each(&:deliver_queue)
      message = "Service Provision started"
      _log.info(message)
      update_and_notify_parent(:message => message)
      queue_post_provision
    end
    destination.update_lifecycle_state
  end

  def queue_post_provision
    _log.info("Queuing post provision of #{self.class.name} id: #{id}, zone: #{my_zone}")

    MiqQueue.put(
      :class_name     => self.class.name,
      :instance_id    => id,
      :method_name    => "do_post_provision",
      :zone           => my_zone,
      :deliver_on     => 1.minute.from_now.utc,
      :tracking_label => tracking_label_id,
      :miq_callback   => {:class_name => self.class.name, :instance_id => id, :method_name => :execute_callback}
    )
  end

  def do_post_provision
    update_request_status
    queue_post_provision unless state == "finished"
  end

  def deliver_to_automate(req_type = request_type, _zone = nil)
    args = {
      :object_type      => self.class.name,
      :object_id        => id,
      :namespace        => "Service/Provisioning/StateMachines",
      :class_name       => "ServiceProvision_Template",
      :instance_name    => req_type,
      :automate_message => "create",
      :attrs            => dialog_values.merge("request" => req_type)
    }

    # Automate entry point overrides from the resource_action
    ra = resource_action

    unless ra.nil?
      if ra.configuration_script_payload
        args[:namespace]        = "Service/Provisioning/StateMachines"
        args[:class_name]       = "ServiceProvision_Template"
        args[:instance_name]    = "CatalogItemInitialization"
        args[:automate_message] = nil
      else
        args[:namespace]        = ra.ae_namespace if ra.ae_namespace.present?
        args[:class_name]       = ra.ae_class     if ra.ae_class.present?
        args[:instance_name]    = ra.ae_instance  if ra.ae_instance.present?
        args[:automate_message] = ra.ae_message   if ra.ae_message.present?
      end

      args[:attrs].merge!(ra.ae_attributes)
    end

    args[:attrs].merge!(MiqAeEngine.create_automation_attributes(destination.class.base_model.name => destination)) if destination.present?
    args[:user_id]      = get_user.id
    args[:miq_group_id] = get_user.current_group.id
    args[:tenant_id]    = get_user.current_tenant.id

    MiqQueue.put(
      :class_name     => 'MiqAeEngine',
      :method_name    => 'deliver',
      :args           => [args],
      :role           => 'automate',
      :zone           => options.fetch(:miq_zone, my_zone),
      :tracking_label => tracking_label_id
    )
    update_and_notify_parent(:state => "pending", :status => "Ok", :message => "Automation Starting")
  end

  def resource_action
    @resource_action ||= source.resource_actions.find_by(:action => 'Provision') if source.respond_to?(:resource_actions)
  end

  def service_resource
    return nil if options[:service_resource_id].blank?

    ServiceResource.find_by(:id => options[:service_resource_id])
  end

  def mark_pending_items_as_finished
    miq_request.miq_request_tasks.each do |s|
      if s.state == 'pending'
        s.update_and_notify_parent(:state => "finished", :status => "Warn", :message => "Error in Request: #{miq_request.id}. Setting pending Task: #{id} to finished.")   unless id == s.id
      end
    end
  end

  def before_ae_starts(_options)
    reload
    if state.to_s.downcase.in?(%w[pending queued])
      _log.info("Executing #{request_class::TASK_DESCRIPTION} request: [#{description}]")
      update_and_notify_parent(:state => "active", :status => "Ok", :message => "In Process")
    end
  end

  def after_ae_delivery(ae_result)
    _log.info("ae_result=#{ae_result.inspect}")
    reload

    return if ae_result == 'retry'
    return if miq_request.state == 'finished'

    if ae_result == 'ok'
      update_and_notify_parent(:state => "finished", :status => "Ok", :message => display_message("#{request_class::TASK_DESCRIPTION} completed"))
    else
      mark_pending_items_as_finished
      update_and_notify_parent(:state => "finished", :status => "Error", :message => display_message("#{request_class::TASK_DESCRIPTION} failed"))
    end
  end

  def update_and_notify_parent(*args)
    prev_state = state
    super
    try("task_#{state}") if prev_state != state
  end

  def task_finished
    service = destination
    return if service.nil?

    service.raise_provisioned_event
    service.update_lifecycle_state if service_template_source?
  end

  private

  def service_template_source?
    source_type == "ServiceTemplate"
  end

  def valid_states
    super + ["provisioned"]
  end
end
