# frozen_string_literal: true

module MesscadaApp
  class RobotHandleScanPalletMovement < BaseService
    attr_reader :robot_interface, :robot, :robot_params, :request, :system_user, :repo, :locn_repo

    MSG_CANCEL = 'Press button [1] to cancel'
    MSG_PALLET = 'Please scan a pallet'
    MSG_LOC = 'Please scan a location'

    def initialize(robot_interface)
      @robot_interface = robot_interface
      @robot = robot_interface.robot
      @robot_params = robot_interface.robot_params
      @request = robot_interface.request
      @system_user = robot_interface.system_user
      @repo = MesscadaRepo.new
      @locn_repo = MasterfilesApp::LocationRepo.new
    end

    def call # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
      AppConst::ROBOT_LOG.info("START ROBOT SCAN (#{robot.system_resource_code}): #{Time.now}") if ENV['ROBODEBUG']

      state = RobotPalletMoveState.new(robot_params[:MAC], scanned_value: robot_params[:barcode])
      robot_status = false
      res = state.process

      feedback = if res.success
                   case state.current_state
                   when :scan_a_pallet
                     lcds = scan_pallet(state)
                   when :stage_pallet
                     location_id = locn_repo.resolve_location_id_from_scan(state.location_scan_value, state.location_scan_field)
                     if location_id.nil_or_empty? || non_child_location?(location_id, state.target_location_id)
                       lcds = [false, state.pallet_number, 'Wrong location', "Please scan into #{state.target_location_code}", MSG_CANCEL]
                     else
                       display_location = repo.get(:locations, %i[print_code location_long_code], location_id).compact.first
                       interactor = FinishedGoodsApp::PalletMovementsInteractor.new(system_user, {}, { route_url: request.path, request_ip: request.ip }, {})
                       res = interactor.move_pallet(state.pallet_number, state.location_scan_value, state.location_scan_field)
                       if res.validation
                         lcds = [false, state.pallet_number, display_location, translate_robot_message(unwrap_failed_response(res)), MSG_CANCEL]
                       elsif !res.success
                         lcds = [false, state.pallet_number, display_location, translate_robot_message(res.message), MSG_CANCEL]
                       else
                         state.reset_state
                         lcds = [true, state.pallet_number, display_location, translate_robot_message(res.message), MSG_PALLET]
                       end
                     end
                   when :move_pallet
                     interactor = FinishedGoodsApp::PalletMovementsInteractor.new(system_user, {}, { route_url: request.path, request_ip: request.ip }, {})
                     location_id = locn_repo.resolve_location_id_from_scan(state.location_scan_value, state.location_scan_field)
                     display_location = if location_id.nil?
                                          state.location_scan_value
                                        else
                                          repo.get(:locations, %i[print_code location_long_code], location_id).compact.first
                                        end
                     res = interactor.move_pallet(state.pallet_number, state.location_scan_value, state.location_scan_field)
                     if res.validation
                       lcds = [false, state.pallet_number, display_location, translate_robot_message(unwrap_failed_response(res)), MSG_CANCEL]
                     elsif !res.success
                       lcds = [false, state.pallet_number, display_location, translate_robot_message(res.message), MSG_CANCEL]
                     else
                       lcds = [true, state.pallet_number, display_location, translate_robot_message(res.message), MSG_PALLET]
                       state.reset_state
                     end
                   else
                     state.reset_state
                     lcds = [false, 'Invalid robot state', 'Resetting...', '', MSG_PALLET]
                   end
                   robot_status = lcds[0]
                   MesscadaApp::RobotFeedback.new(device: robot.system_resource_code,
                                                  status: lcds[0],
                                                  line1: lcds[1],
                                                  line2: lcds[2],
                                                  line3: lcds[3],
                                                  line4: lcds[4])
                 else
                   robot_status = false
                   MesscadaApp::RobotFeedback.new(device: robot.system_resource_code,
                                                  status: false,
                                                  line1: 'ERROR',
                                                  line2: translate_robot_message(res.message),
                                                  line3: %i[move_pallet stage_pallet].include?(state.current_state) ? MSG_LOC : MSG_PALLET,
                                                  line4: %i[move_pallet stage_pallet].include?(state.current_state) ? MSG_CANCEL : '')
                 end

      AppConst::ROBOT_LOG.info("PRE-FEEDBACK ROBOT SCAN (#{robot.system_resource_code}): #{Time.now}") if ENV['ROBODEBUG']
      robot_interface.respond(feedback, robot_status, orange: %i[move_pallet stage_pallet].include?(state.current_state))
    rescue Crossbeams::InfoError, StandardError => e
      detail = e.is_a?(Crossbeams::InfoError) ? 'INFO: ' : ''
      ErrorMailer.send_exception_email(e, subject: "#{detail}#{self.class.name}", message: robot_interface.decorate_mail_message('pallet_movement'))
      puts e.message
      puts e.backtrace.join("\n")
      feedback = MesscadaApp::RobotFeedback.new(device: robot.system_resource_code,
                                                status: false,
                                                short1: 'System error',
                                                short2: e.message)
      robot_interface.respond(feedback, false)
    end

    private

    # Save the scanned pallet state
    def scan_pallet(state)
      pallet_id = repo.get_id(:pallets, pallet_number: state.pallet_number)
      return [false, 'Invalid pallet number', MSG_PALLET, '', ''] if pallet_id.nil?

      target_msg = ''
      loc = location_for_pallet_to_be_staged(pallet_id)
      if loc
        target_msg = "Scan into #{loc[:location_code]}"
        state.pallet_scanned_for_staging(pallet_id, loc[:location_code], loc[:location_id])
      else
        state.pallet_scanned_for_move(pallet_id)
      end
      [true, state.pallet_number, MSG_LOC, target_msg, MSG_CANCEL]
    end

    # Is the pallet staged to be scanned into a particular location?
    def location_for_pallet_to_be_staged(pallet_id)
      locs = repo.staging_location_for_pallet(pallet_id)
      return nil if locs.nil?

      { location_id: locs.first, location_code: locs[1] || locs[2] }
    end

    # Take some expected messages and modify them to fit in the robot's limited display.
    def translate_robot_message(msg) # rubocop:disable Metrics/CyclomaticComplexity
      match = msg.match(/PALLET: .+ moved successfully/i)
      return 'Moved successfully' if match

      match = msg.match(/Scan (.+) is not a recognised pallet number length/i)
      return "#{match[1]} not a pallet" if match

      match = msg.match(/is already in this location/i)
      return 'Already in this location' if match

      match = msg.match(/Validation error - (.+)/i)
      return match[1].capitalize if match

      match = msg.match(/is on a tripsheet/i)
      return 'Pallet is on a tripsheet' if match

      match = msg.match(/Maximun units in location have been reached/i)
      return 'Location is full' if match

      msg
    end

    def non_child_location?(location_id, target_location_id)
      !locn_repo.belongs_to_parent?(location_id, target_location_id)
    end
  end
end
