module MesscadaApp
  class ForceBinStaging < BaseService
    attr_reader :repo, :delivery_repo, :locn_repo, :messcada_repo, :bin_number, :plant_resource_code, :presort_staging_run_id, :presort_staging_run_child_id, :bin, :staging_run

    def initialize(bin_number, child_run_id)
      @repo = RawMaterialsApp::PresortStagingRunRepo.new
      @delivery_repo = RawMaterialsApp::RmtDeliveryRepo.new
      @locn_repo = MasterfilesApp::LocationRepo.new
      @messcada_repo = MesscadaApp::MesscadaRepo.new
      @bin_number = bin_number
      @presort_staging_run_child_id = child_run_id
      @presort_staging_run_id = @repo.get(:presort_staging_run_children, :presort_staging_run_id, child_run_id)
      @staging_run = repo.find_presort_staging_run_flat(@presort_staging_run_id)
      @plant_resource_code = @staging_run[:plant_resource_code]
    end

    def call
      errors = valid_bin?(bin_number)
      return failed_response(errors.join(', ')) if errors

      repo.transaction do
        StageBins.call([bin_number], plant_resource_code, presort_staging_run_child_id)

        success_response("Bin: #{bin_number} staged against run: #{presort_staging_run_id}, child: #{presort_staging_run_child_id} successfully")
      end
    rescue StandardError => e
      failed_response(e.message)
    end

    private

    def valid_bin?(asset_number) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
      err = bin_exists?(asset_number)
      return [err] unless err.nil?

      errors = []
      err = validate_location(asset_number)
      errors << err unless err.nil?

      check = messcada_repo.check_bin_in_wip(asset_number)
      errors << check.message  unless check.success

      delivery_id = repo.get_value(:rmt_bins, :rmt_delivery_id, bin_asset_number: asset_number)
      unless delivery_id.nil_or_empty?
        res = QualityApp::FailedAndPendingMrlResults.call(delivery_id)
        errors << res.message unless res.success
      end

      errs = valid_bin_for_active_parent_run?
      errors += errs unless errs.nil?

      err = rebin?(asset_number)
      errors << err unless err.nil?

      err = not_on_sale?(asset_number)
      errors << err unless err.nil?

      err = valid_bin_for_active_child_run?(asset_number)
      errors << err unless err.nil?

      errors unless errors.empty?
    end

    def rebin?(asset_number)
      return "Bin: #{asset_number} is a rebin" if bin[:production_run_rebin_id]
    end

    def not_on_sale?(asset_number)
      return "Bin: #{asset_number} is on sale" if bin[:bin_load_product_id]
    end

    def bin_exists?(asset_number)
      @bin = repo.find_bin_record_by_asset_number(asset_number)
      return "Bin:#{asset_number} does not exist" unless bin
      return "Bin:#{asset_number} has been tipped" unless bin[:tipped_asset_number].nil_or_empty?
      return "Bin:#{asset_number} has been shipped" unless bin[:shipped_asset_number].nil_or_empty?
    end

    def validate_location(asset_number)
      AppConst::CR_RMT.invalid_presort_bin_staging_locations.each do |l|
        locn_id = repo.get_value(:locations, :id, location_long_code: l)
        return "Invalid bin location. Bin:#{asset_number} is from #{l}" if locn_repo.belongs_to_parent?(bin[:location_id], locn_id)
      end
      nil
    end

    def valid_bin_for_active_child_run?(asset_number)
      run_farm_code = repo.child_run_farm(presort_staging_run_child_id)
      bin_farm_code = repo.bin_farm(asset_number)
      return "Bin:#{asset_number} belongs to farm [#{bin_farm_code}], but child_run's farm is [#{run_farm_code}]." unless bin_farm_code == run_farm_code || run_farm_code == '0P'
    end

    VALID_BIN_ERROR_MSG = '%s is %s on bin, but %s on run.'.freeze

    def valid_bin_for_active_parent_run? # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
      errs = []
      errs << format(VALID_BIN_ERROR_MSG, 'season_code', bin.season_code || 'blank', staging_run.season_code || 'blank') if bin.season_id != staging_run.season_id
      errs << format(VALID_BIN_ERROR_MSG, 'cultivar_name', bin.cultivar_name || 'blank', staging_run.cultivar_name || 'blank') if bin.cultivar_id != staging_run.cultivar_id
      errs << format(VALID_BIN_ERROR_MSG, 'colour', bin.colour_percentage || 'blank', staging_run.colour_percentage || 'blank') if staging_run.colour_percentage_id && bin.colour_percentage_id != staging_run.colour_percentage_id
      errs << format(VALID_BIN_ERROR_MSG, 'cold_treatment_code', bin.actual_cold_treatment_code || 'blank', staging_run.actual_cold_treatment_code || 'blank') if staging_run.actual_cold_treatment_id && bin.actual_cold_treatment_id != staging_run.actual_cold_treatment_id
      errs << format(VALID_BIN_ERROR_MSG, 'ripeness_treatment_code', bin.actual_ripeness_treatment_code || 'blank', staging_run.actual_ripeness_treatment_code || 'blank') if staging_run.actual_ripeness_treatment_id && bin.actual_ripeness_treatment_id != staging_run.actual_ripeness_treatment_id
      errs << format(VALID_BIN_ERROR_MSG, 'rmt_code', bin.rmt_code || 'blank', staging_run.rmt_code || 'blank') if staging_run.rmt_code_id && bin.rmt_code_id != staging_run.rmt_code_id
      errs << format(VALID_BIN_ERROR_MSG, 'rmt_class_code', bin.class_code || 'blank', staging_run.rmt_class_code || 'blank') if staging_run.rmt_class_id && bin.rmt_class_id != staging_run.rmt_class_id
      errs << format(VALID_BIN_ERROR_MSG, 'size_code', bin.size_code || 'blank', staging_run.size_code || 'blank') if staging_run.rmt_size_id && bin.rmt_size_id != staging_run.rmt_size_id
      errs << "bin location_code[#{bin.location_long_code}] is not in [#{AppConst::CR_RMT.valid_presort_bin_staging_locations.join(' ')}]" unless valid_bin_location_for_staging?
      return errs unless errs.empty?
    end

    def valid_bin_location_for_staging?
      AppConst::CR_RMT.valid_presort_bin_staging_locations.each do |l|
        locn_id = repo.get_value(:locations, :id, location_long_code: l)
        return true if locn_repo.belongs_to_parent?(bin[:location_id], locn_id)
      end
      false
    end
  end
end
