module Ddr::Batch

  class IngestBatchObject < BatchObject

    def local_validations
      errors = []
      errors << "#{@error_prefix} Model required for INGEST operation" unless model
      errors += validate_pre_assigned_pid if pid
      errors += validate_collection if model == 'Collection'
      errors
    end

    def model_datastream_keys
      model.constantize.new.datastreams.keys
    end

    def process(user, opts = {})
      ingest(user, opts) unless verified
    end

    def results_message
      if pid
        message_level = verified ? Logger::INFO : Logger::WARN
        verification_result = verified ? "Verified" : "VERIFICATION FAILURE"
        ProcessingResultsMessage.new(message_level, "Ingested #{model} #{identifier} into #{pid}...#{verification_result}")
      else
        ProcessingResultsMessage.new(Logger::ERROR, "Attempt to ingest #{model} #{identifier} FAILED")
      end
    end

    private

    def validate_pre_assigned_pid
      errs = []
      errs << "#{@error_prefix} #{pid} already exists in repository" if ActiveFedora::Base.exists?(pid)
      return errs
    end

    def validate_collection
      errs = []
      coll = Collection.new
      batch_object_attributes.each { |attr| coll = add_attribute(coll, attr) }
      unless coll.valid?
        coll.errors.messages.each { |k, v| errs << "#{@error_prefix} Collection #{k} #{v.join(';')}" }
      end
      errs
    end

    def ingest(user, opts = {})
      repo_object = create_repository_object(user)
      if !repo_object.nil? && !repo_object.new_record?
        ingest_outcome_detail = []
        ingest_outcome_detail << "Ingested #{model} #{identifier} into #{repo_object.pid}"
        Ddr::Events::IngestionEvent.new.tap do |event|
          event.object = repo_object
          event.user = user
          event.summary = EVENT_SUMMARY % {
            :label => "Object ingestion",
            :batch_id => id,
            :identifier => identifier,
            :model => model
          }
          event.detail = ingest_outcome_detail.join("\n")
          event.save!
        end
        update_attributes(:pid => repo_object.pid)
        verifications = verify_repository_object
        verification_outcome_detail = []
        verified = true
        verifications.each do |key, value|
          verification_outcome_detail << "#{key}...#{value}"
          verified = false if value.eql?(VERIFICATION_FAIL)
        end
        update_attributes(:verified => verified)
        Ddr::Events::ValidationEvent.new.tap do |event|
          event.object = repo_object
          event.failure! unless verified
          event.summary = EVENT_SUMMARY % {
            :label => "Object ingestion validation",
            :batch_id => id,
            :identifier => identifier,
            :model => model
          }
          event.detail = verification_outcome_detail.join("\n")
          event.save!
        end
      else
        verifications = nil
      end
      repo_object
    end

    def create_repository_object(user)
      repo_pid = pid if pid.present?
      repo_object = nil
      begin
        repo_object = model.constantize.new(:pid => repo_pid)
        repo_object.label = label if label
        batch_object_attributes.each { |a| repo_object = add_attribute(repo_object, a) }
        repo_object.save(validate: false, skip_structure_updates: true, user: user)
        batch_object_datastreams.each { |d| repo_object = populate_datastream(repo_object, d) }
        batch_object_relationships.each { |r| repo_object = add_relationship(repo_object, r) }
        batch_object_roles.each { |r| repo_object = add_role(repo_object, r) }
        repo_object.save!(skip_structure_updates: true)
      rescue Exception => e1
        logger.fatal("Error in creating repository object #{repo_object.pid} for #{identifier} : #{e1}")
        if repo_object && !repo_object.new_record?
          begin
            logger.info("Deleting potentially incomplete #{repo_object.pid} due to error in ingest batch processing")
            repo_object.destroy
          rescue Exception => e2
            logger.fatal("Error deleting repository object #{repo_object.pid}: #{e2}")
          end
        end
        raise e1
      end
      repo_object
    end

  end

end
