require_relative "bulk_import_parser"
require_relative "bulk_import_report"

class TopContainerLinker < BulkImportParser
  include BulkImportMixins

  #ASpace field headers row indicator
  START_MARKER = /ArchivesSpace field code/.freeze

  attr_reader :report

  def initialize(input_file, content_type, current_user, opts)
    init_handlers = false
    if (opts[:initialize_enums])
      opts.delete(:initialize_enums)
      init_handlers = true
    end
    super(input_file, content_type, current_user, opts, nil)
    @resource_ref = "/repositories/#{@opts[:repo_id]}/resources/#{@opts[:rid]}"
    @start_marker = START_MARKER
    @counter = 0
    if (init_handlers)
      initialize_handler_enums()
    end
  end

  def initialize_handler_enums
    @cih = ContainerInstanceHandler.new(@current_user, @validate_only)
  end

  # save (create/update) the archival object, then revive it
  def ao_save(ao)
    revived = nil
    begin
      archObj = nil
      if ao.id.nil?
        archObj = ArchivalObject.create_from_json(ao)
      else
        obj = ArchivalObject.get_or_die(ao.id)
        archObj = obj.update_from_json(ao)
      end
      objs = ArchivalObject.sequel_to_jsonmodel([archObj])
      revived = objs[0] if !objs.empty?
    rescue ValidationException => ve
      raise BulkImportException.new(I18n.t("bulk_import.error.ao_validation", :err => ve.errors))
    rescue Exception => e
      Log.error("UNEXPECTED ao save error: #{e.message}\n#{e.backtrace}")
      Log.error(ASUtils.jsonmodels_to_hashes(ao).pretty_inspect) if ao
      raise e
    end
    revived
  end

  # look for all the required fields to make sure they are legit
  def process_row(row_hash = nil)
    #This allows the processing of a single row
    if (!row_hash.nil?)
      @row_hash = row_hash
      @report = BulkImportReport.new
      @counter += 1
      @report.new_row(@counter)
    end
    err_arr = []
    begin
      ao = nil
      # Check that the archival object ref id exists
      ref_id = @row_hash["ref_id"]

      if ref_id.nil?
        err_arr.push I18n.t("top_container_linker.error.ref_id_miss", :row_num => @counter.to_s)
      else
        #Check that the AO can be found in the db
        ao = archival_object_from_ref(ref_id.strip)
        if (ao.nil?)
          err_arr.push I18n.t("top_container_linker.error.ao_not_in_db", :ref_id => ref_id.to_s, :row_num => @counter.to_s)
        else
          @report.add_archival_object(ao)
        end
      end

      #If the AO is nil, then just stop here before checking anything else
      if ao.nil?
        raise BulkImportException.new("No AO found;" + err_arr)
      end

      ead_id = @row_hash["ead_id"]
      if ead_id.nil?
        err_arr.push I18n.t("top_container_linker.error.ead_id_miss", :ref_id => ref_id.to_s, :row_num => @counter.to_s)
      else
        #Check that the AO can be found in the db
        resource = resource_from_ref(ead_id.strip)
        if (resource.nil?)
          err_arr.push I18n.t("top_container_linker.error.resource_not_in_db", :ead_id => ead_id.to_s, :row_num => @counter.to_s)
        elsif (resource.uri != @resource_ref)
          err_arr.push I18n.t("top_container_linker.error.resources_do_not_match", :spreadsheet_resource => resource.uri, :ead_id => ead_id.to_s, :current_resource => @resource_ref, :row_num => @counter.to_s)
        end
      end
      #Check that the instance type exists
      instance_type = @row_hash["instance_type"]
      if instance_type.nil?
        err_arr.push I18n.t("top_container_linker.error.instance_type_miss", :ref_id => ref_id.to_s, :row_num => @counter.to_s)
      end
      #Check that either the Top Container Indicator or Top Container Record No. is present
      tc_indicator = @row_hash["top_container_indicator"]
      tc_record_no = @row_hash["top_container_id"]
      #Both missing
      if (tc_indicator.nil? && tc_record_no.nil?)
        err_arr.push I18n.t("top_container_linker.error.tc_indicator_and_record_no_miss", :ref_id => ref_id.to_s, :row_num => @counter.to_s)
      end
      #Both exist
      if (!tc_indicator.nil? && !tc_record_no.nil?)
        err_arr.push I18n.t("top_container_linker.error.tc_indicator_and_record_no_exist", :ref_id => ref_id.to_s, :row_num => @counter.to_s)
      end
      #Container type/Container indicator combo already exists
      tc_type = @row_hash["top_container_type"]
      tc_instance = nil
      if (!tc_indicator.nil? && !tc_type.nil?)
        barcode = @row_hash["top_container_barcode"]
        tc_jsonmodel_obj = @cih.get_top_container_json_from_hash(tc_type, tc_indicator, barcode, @resource_ref)
        display_indicator = tc_indicator;
        if (tc_jsonmodel_obj.nil?)
          #Create new TC
          tc_instance = create_top_container_instance(instance_type, tc_indicator, tc_type, err_arr, ref_id, @counter.to_s)
        else
          tc_instance = create_top_container_instance(instance_type, tc_jsonmodel_obj.indicator, tc_jsonmodel_obj.type, err_arr, ref_id, @counter.to_s)
          display_indicator = tc_jsonmodel_obj.indicator
        end
      elsif (!tc_record_no.nil?)
        tc_jsonmodel_obj = TopContainer.get_or_die(tc_record_no.strip.to_i)
        if tc_jsonmodel_obj.nil?
          #Cannot find TC record with ID
          err_arr.push I18n.t("top_container_linker.error.tc_record_no_missing", :tc_id=> tc_record_no, :ref_id => ref_id.to_s, :row_num => @counter.to_s)
        else
          child_type = @row_hash["child_type"]
          child_indicator = @row_hash["child_indicator"]
          barcode_2 = @row_hash["child_barcode"]
          subcontainer = {}
          if (!child_type.nil? && !child_indicator.nil?)
            subcontainer = { "type_2" => child_type.strip,
                            "indicator_2" => child_indicator.strip,
                            "barcode_2" => barcode_2}
          end
          tc_instance = @cih.format_container_instance(instance_type, tc_jsonmodel_obj, subcontainer)
          display_indicator = tc_jsonmodel_obj.indicator
        end
      end
      if (!tc_instance.nil?)
        ao.instances ||= []
        ao.instances << tc_instance
        @report.add_info("Adding Top Container Instance " + instance_type.capitalize + " " + display_indicator + " to Archival Object " + ref_id)
        ao_save(ao)
      end
    rescue StopBulkImportException => se
      err_arr.join("; ")
      raise StopBulkImportException.new(se.message + "; " + err_arr)
    rescue BulkImportException => te
      err_arr.join("; ")
      raise BulkImportException.new(te.message + "; " + err_arr)
    rescue Exception => e
      Log.error(["UNEXPLAINED EXCEPTION on process_row", e.message, e.backtrace, @row_hash].pretty_inspect)
    end
    if !err_arr.empty?
      raise BulkImportException.new(err_arr.join("; "))
    end
    ao
  end

  def create_top_container_instance(instance_type, indicator, type, err_arr, ref_id, row_num)
    #Find the top container with this indicator and type if it exists
    barcode = @row_hash["top_container_barcode"]
    tc_obj = @cih.get_top_container_json_from_hash(type, indicator, barcode, @resource_ref)
    #Check if the location ID can be found in the db
    child_type = @row_hash["child_type"]
    child_indicator = @row_hash["child_indicator"]
    barcode_2 = @row_hash["child_barcode"]
    subcontainer = {}
    if (!child_type.nil? && !child_indicator.nil?)
      subcontainer = { "type_2" => child_type.strip,
                      "indicator_2" => child_indicator.strip,
                      "barcode_2" => barcode_2}
    end

    instance = nil
    begin
      if (!tc_obj.nil?)
        #We may have created a new TC already during the iteration so only
        #grab the instance data from the container instance handler (@cih) if that is the case
        instance = @cih.format_container_instance(instance_type, tc_obj, subcontainer)
      else
        instance = @cih.create_container_instance(instance_type, type, indicator, barcode, @resource_ref, @report, subcontainer)
      end
    rescue Exception => e
      @report.add_errors(I18n.t("top_container_linker.error.no_tc", :ref_id => ref_id.to_s, :row_num => row_num, :why => e.message))
      instance = nil
    end


    #If we created a new Top Container, then add the location and cp if they exist.
    if (tc_obj.nil? && !instance.nil?)
      #Get the top container that was just created
      tc_id = instance["sub_container"]["top_container"]["ref"].split('/')[4]
      tc_obj = TopContainer.get_or_die(tc_id.to_i)
      if (tc_obj.nil?)
        raise BulkImportException.new(I18n.t("top_container_linker.error.no_tc", :ref_id => ref_id.to_s, :row_num => row_num, :why => "Could not find newly created Top Container"))
      end
      now = Time.now

      #Check if the location ID can be found in the db
      loc_id = @row_hash["location_id"]
      if (!loc_id.nil?)
        loc = Location.get_or_die(loc_id.strip.to_i)
        if (loc.nil?)
          err_arr.push I18n.t("top_container_linker.error.loc_not_in_db", :loc_id=> loc_id.to_s, :ref_id => ref_id.to_s, :row_num => row_num)
        else
          begin
            loc_relationship = TopContainer.find_relationship(:top_container_housed_at)
            loc_relationship.relate(tc_obj, loc, {
              :status => 'current',
              :start_date => now.iso8601,
              :aspace_relationship_position => 0,
              :system_mtime => now,
              :user_mtime => now
            })
          rescue Exception => e
            @report.add_errors(I18n.t("top_container_linker.error.problem_adding_current_location", :ref_id => ref_id.to_s, :row_num => row_num, :why => e.message))
            instance = nil
          end
        end
      end

      #Check if Container Profile Record No. can be found in the db
      cp_id = @row_hash["container_profile_id"]
      if (!cp_id.nil?)
        cp = ContainerProfile.get_or_die(cp_id.strip.to_i)
        if (cp.nil?)
          err_arr.push I18n.t("top_container_linker.error.cp_not_in_db", :cp_id=> cp_id.to_s, :ref_id => ref_id.to_s, :row_num => row_num)
        else
          begin
            cp_relationship = TopContainer.find_relationship(:top_container_profile)
            cp_relationship.relate(tc_obj, cp, {
              :aspace_relationship_position => 1,
              :system_mtime => now,
              :user_mtime => now
            })
          rescue Exception => e
            @report.add_errors(I18n.t("top_container_linker.error.problem_setting_container_profile", :ref_id => ref_id.to_s, :row_num => row_num, :why => e.message))
            instance = nil
          end
        end
      end
    end

    instance
  end

end
