class Foodcourt::QrpayController < BaseFoodcourtController
  class PaymentProcessingError < StandardError; end

  require 'rqrcode'
  skip_before_action :authenticate, only: [:create]
  skip_before_action :verify_authenticity_token, only: [:create]

  def precreate
    @cash_exist = PaymentMethodSetting.cash_exist
    @credit_exist = PaymentMethodSetting.credit_exist
    display_type = Lookup.find_by_lookup_type("display_type")
    if !display_type.nil? && display_type.value.to_i == 2
      @display_type = display_type.value
    else
      @display_type = nil
    end
    path = request.fullpath
    sale_id = params[:sale_id]
    @trans_flag = true

    @cashier_type = params[:cashier_type] || session[:cashier_type] || "food_court"
    Rails.logger.info "Precreate action called with params: #{@cashier_type}"

    # if params[:type] == "transaction"
    #   @trans_flag = false
    #   @cashier_type   = "cashier"
    # else
    #   @cashier_type   = params[:type]
    # end

    if path.include? ("credit_payment")
      @sale_payment = SalePayment.get_credit_amount_due_left(sale_id)
    end

    @member_discount = MembershipSetting.find_by_discount(1)
    @membership_rebate_balance=0

    @changable_tax = true
    lookup_changable_tax = Lookup.collection_of('changable_tax')
    if !lookup_changable_tax.empty?
      lookup_changable_tax.each do |changable_tax|
        if changable_tax[0].downcase == "change"
          if changable_tax[1] == '0'
            @changable_tax = false
          end
        end
      end
    end

    if Sale.exists?(sale_id)
      begin
        @cash = 0.0
        @kbz_pay_amount = 0.0
        @other = 0.0
        @ppamount = 0.0
        @visacount= 0.0
        @jcbcount= 0.0
        @mastercount = 0.0
        @unionpaycount = 0.0
        @alipaycount = 0.0
        @junctionpaycount = 0.0
        @credit = 0.0
        @paymalcount = 0.0
        @dingacount = 0.0
        @giftvouchercount = 0.0
        @sale_data = Sale.find_by_sale_id(sale_id)
        @balance = 0
        @accountable_type = ''
        @table_no = ''
        @dining  = ''
        @other_payment = 0.0
        @pdf_view = nil
        @lookup_pdf = Lookup.find_by_lookup_type("ReceiptPdfView")
        if !@lookup_pdf.nil?
          @pdf_view = @lookup_pdf.value
        end

        amount = SalePayment.get_kbz_pay_amount(sale_id, current_user)
        @kbz_pay_amount += amount.to_f

        # @shop        =  shop_detail  #show shop info

        @customer_lists = Customer.where(name: ["WALK-IN", "TAKEAWAY"])
        saleObj = Sale.find(sale_id)

        #total customer with individual total amount
        @individual_total = Array.new
        if !saleObj.equal_persons.nil?
            per_person_amount = saleObj.grand_total.to_f / saleObj.equal_persons.to_i
            @individual_total.push({'total_customer' => saleObj.equal_persons.to_i, 'per_person_amount' => per_person_amount.to_f })
        end

        if current_shop.is_rounding_adj
          a = saleObj.grand_total % 25 # Modulus
          b = saleObj.grand_total / 25 # Division
          #not calculate rounding if modulus is 0 and division is even
          #calculate rounding if modulus is zero or not zero and division are not even
          if (a != 0.0 && b%2 != 0.0) || (a==0.0 && b%2 !=0)
            new_total    = Sale.get_rounding_adjustment(saleObj.grand_total)
            @rounding_adj = new_total-saleObj.grand_total
            saleObj.update_attributes(grand_total: new_total,old_grand_total: saleObj.grand_total,rounding_adjustment:@rounding_adj)
            @sale_data.grand_total = new_total
            @sale_data.old_grand_total = saleObj.grand_total
            @sale_data.rounding_adjustment = @rounding_adj
          else
            @rounding_adj = @sale_data.rounding_adjustment
          end
        else
          @rounding_adj = @sale_data.rounding_adjustment
        end
      #end rounding adjustment

      # get printer info
        @print_settings = PrintSetting.get_precision_delimiter()

        #get customer amount
        @customer = Customer.find(@sale_data.customer_id)
        # accounts = @customer.tax_profiles
        accounts = TaxProfile.where("group_type = ?",@cashier_type).order("order_by ASC")
        @account_arr =[]
        @tax_arr =[]
        accounts.each do |acc|
          account = TaxProfile.find(acc.id)
          # @account_arr.push(account)
          @tax_arr.push(account.name)
        end
        sale_taxes = SaleTax.where("sale_id = ?", saleObj.sale_id)
        if !sale_taxes.empty?
          sale_taxes.each do |sale_tax|
            @account_arr.push(sale_tax)
          end
        end
        rebate = MembershipSetting.find_by_rebate(1)
        # get member information
        if @customer.membership_id != nil && rebate
          response = Customer.get_member_account(@customer)
          if response["status"]==true
          response["account_data"].each do |res|
            if res["accountable_type"] == "RebateAccount" || res["accountable_type"] == "RebatebonusAccount"
              @balance = @balance.to_f + res["balance"].to_f
              #  @accountable_type = res["accountable_type"]
              @accountable_type = "Rebate Balance"
            end
          end
          end
        end

        #end customer amount

        #paymal payment

        @sale_data.bookings.each do |sbk|
          if sbk.dining_facility_id.to_i >0
            df = DiningFacility.find(sbk.dining_facility_id)
            @table_no = df.type + ' ' + df.name
            @checkin_time = sbk.checkin_at
            @dining = df
            break
          else
            @table_no = nil
            @checkin_time = nil
            @dining = nil
          end
        end

        if path.include? ("credit_payment")
          @sale_payment_data = SalePayment.get_sale_payment_for_credit(@sale_data)
        else
          @sale_payment_data = SalePayment.get_sale_payments(@sale_data)
        end
        @sale_payment_data.each do |spay|
          if spay.payment_method == "cash"
          @cash += spay.payment_amount
          end
          if spay.payment_method !="creditnote"
            @other_payment += spay.payment_amount
          end

          if spay.payment_method == "mpu"
            @other += spay.payment_amount
          elsif spay.payment_method == "paypar"
            @ppamount += spay.payment_amount
          elsif spay.payment_method == "visa"
            @visacount += spay.payment_amount
          elsif spay.payment_method == "jcb"
            @jcbcount += spay.payment_amount
          elsif spay.payment_method == "master"
            @mastercount += spay.payment_amount
          elsif spay.payment_method == "unionpay"
            @unionpaycount += spay.payment_amount
          elsif spay.payment_method == "JunctionPay"
            @junctionpaycount += spay.payment_amount
          elsif spay.payment_method == "creditnote"
            @credit += spay.payment_amount
          elsif spay.payment_method == "paymal"
            @paymalcount += spay.payment_amount
          elsif spay.payment_method == "alipay"
            @alipaycount += spay.payment_amount
          elsif spay.payment_method == "dinga"
            @dingacount += spay.payment_amount
          elsif spay.payment_method == "giftvoucher"
            @giftvouchercount += spay.payment_amount
          end
        end

      end
    end
  end

  def init
    @cash_exist = PaymentMethodSetting.cash_exist
    @credit_exist = PaymentMethodSetting.credit_exist
    display_type = Lookup.find_by_lookup_type("display_type")
     if !display_type.nil? && display_type.value.to_i == 2
        @display_type = display_type.value
      else
        @display_type = nil
     end
    path = request.fullpath
    sale_id = params[:sale_id]
    @trans_flag = true
    if params[:type] == "transaction"
      @trans_flag = false
      @cashier_type   = "cashier"
    else
      @cashier_type   = params[:type]
    end

    if path.include? ("credit_payment")
      @sale_payment = SalePayment.get_credit_amount_due_left(sale_id)
    end

    @member_discount = MembershipSetting.find_by_discount(1)
    @membership_rebate_balance=0

    if Sale.exists?(sale_id)
      begin
        @cash = 0.0
        @kbz_pay_amount = 0.0
        @other = 0.0
        @ppamount = 0.0
        @visacount= 0.0
        @jcbcount= 0.0
        @mastercount = 0.0
        @unionpaycount = 0.0
        @alipaycount = 0.0
        @junctionpaycount = 0.0
        @credit = 0.0
        @paymalcount = 0.0
        @dingacount = 0.0
        @giftvouchercount = 0.0
        @sale_data = Sale.find_by_sale_id(sale_id)
        @balance = 0
        @accountable_type = ''
        @table_no = ''
        @dining  = ''
        @other_payment = 0.0
        @pdf_view = nil
        @lookup_pdf = Lookup.find_by_lookup_type("ReceiptPdfView")
        if !@lookup_pdf.nil?
          @pdf_view = @lookup_pdf.value
        end

        amount = SalePayment.get_kbz_pay_amount(sale_id, current_user)
        @kbz_pay_amount += amount.to_f

        # @shop        =  shop_detail  #show shop info

        @customer_lists = Customer.where(name: ["WALK-IN", "TAKEAWAY"])
        saleObj = Sale.find(sale_id)

        #total customer with individual total amount
        @individual_total = Array.new
        if !saleObj.equal_persons.nil?
            per_person_amount = saleObj.grand_total.to_f / saleObj.equal_persons.to_i
            @individual_total.push({'total_customer' => saleObj.equal_persons.to_i, 'per_person_amount' => per_person_amount.to_f })
        end

        if current_shop.is_rounding_adj
          a = saleObj.grand_total % 25 # Modulus
          b = saleObj.grand_total / 25 # Division
          #not calculate rounding if modulus is 0 and division is even
          #calculate rounding if modulus is zero or not zero and division are not even
          if (a != 0.0 && b%2 != 0.0) || (a==0.0 && b%2 !=0)
            new_total    = Sale.get_rounding_adjustment(saleObj.grand_total)
            @rounding_adj = new_total-saleObj.grand_total
            saleObj.update_attributes(grand_total: new_total,old_grand_total: saleObj.grand_total,rounding_adjustment:@rounding_adj)
            @sale_data.grand_total = new_total
            @sale_data.old_grand_total = saleObj.grand_total
            @sale_data.rounding_adjustment = @rounding_adj
          else
            @rounding_adj = @sale_data.rounding_adjustment
          end
        else
          @rounding_adj = @sale_data.rounding_adjustment
        end
      #end rounding adjustment

      # get printer info
        @print_settings = PrintSetting.get_precision_delimiter()

        #get customer amount
        @customer = Customer.find(@sale_data.customer_id)
        # accounts = @customer.tax_profiles
        accounts = TaxProfile.where("group_type = ?",@cashier_type).order("order_by ASC")
        @account_arr =[]
        @tax_arr =[]
        accounts.each do |acc|
          account = TaxProfile.find(acc.id)
          # @account_arr.push(account)
          @tax_arr.push(account.name)
        end
        sale_taxes = SaleTax.where("sale_id = ?", saleObj.sale_id)
        if !sale_taxes.empty?
          sale_taxes.each do |sale_tax|
            @account_arr.push(sale_tax)
          end
        end
        rebate = MembershipSetting.find_by_rebate(1)
        # get member information
        if @customer.membership_id != nil && rebate
          response = Customer.get_member_account(@customer)
          if response["status"]==true
          response["account_data"].each do |res|
            if res["accountable_type"] == "RebateAccount" || res["accountable_type"] == "RebatebonusAccount"
              @balance = @balance.to_f + res["balance"].to_f
              #  @accountable_type = res["accountable_type"]
              @accountable_type = "Rebate Balance"
            end
          end
          end
        end

        #end customer amount

        #paymal payment

        @sale_data.bookings.each do |sbk|
          if sbk.dining_facility_id.to_i >0
            df = DiningFacility.find(sbk.dining_facility_id)
            @table_no = df.type + ' ' + df.name
            @checkin_time = sbk.checkin_at
            @dining = df
            break
          else
            @table_no = nil
            @checkin_time = nil
            @dining = nil
          end
        end

        if path.include? ("credit_payment")
          @sale_payment_data = SalePayment.get_sale_payment_for_credit(@sale_data)
        else
          @sale_payment_data = SalePayment.get_sale_payments(@sale_data)
        end
        @sale_payment_data.each do |spay|
          if spay.payment_method == "cash"
          @cash += spay.payment_amount
          end
          if spay.payment_method !="creditnote"
            @other_payment += spay.payment_amount
          end

          if spay.payment_method == "mpu"
            @other += spay.payment_amount
          elsif spay.payment_method == "paypar"
            @ppamount += spay.payment_amount
          elsif spay.payment_method == "visa"
            @visacount += spay.payment_amount
          elsif spay.payment_method == "jcb"
            @jcbcount += spay.payment_amount
          elsif spay.payment_method == "master"
            @mastercount += spay.payment_amount
          elsif spay.payment_method == "unionpay"
            @unionpaycount += spay.payment_amount
          elsif spay.payment_method == "JunctionPay"
            @junctionpaycount += spay.payment_amount
          elsif spay.payment_method == "creditnote"
            @credit += spay.payment_amount
          elsif spay.payment_method == "paymal"
            @paymalcount += spay.payment_amount
          elsif spay.payment_method == "alipay"
            @alipaycount += spay.payment_amount
          elsif spay.payment_method == "dinga"
            @dingacount += spay.payment_amount
          elsif spay.payment_method == "giftvoucher"
            @giftvouchercount += spay.payment_amount
          end
        end

        @paymethod = PaymentMethodSetting.find_by(payment_method: "MMQR")

        if @paymethod.nil?
          raise "MMQR payment method not configured"
        end

        @merchant = KbzMerchant.new(@paymethod)

        @response = @merchant.create_order(amount: @sale_data.grand_total, merch_order_id: @sale_data.receipt_no)
        case @response[:status]
        when 'success'
          @qr_string = @response[:data]["qrCode"]
          qrcode = RQRCode::QRCode.new(@qr_string)

          @qr_svg = qrcode.as_svg(
            color: "000",
            shape_rendering: "crispEdges",
            module_size: 3,
            standalone: true,
            use_path: true
          )
          ActionCable.server.broadcast('second_display_view_channel', { data: @qr_string, qr_svg: @qr_svg, grand_total: @sale_data.grand_total, invoice_no: @sale_data.receipt_no })

        when 'error'
          @error = @response[:message]
          raise PaymentProcessingError, @response[:message]
        when 'failed'
          @error = @response[:message]
          raise PaymentProcessingError, @response[:message]
        end
      rescue PaymentProcessingError, StandardError => e
        Rails.logger.error("Payment processing error: #{e.message}")
        cancel_order_and_sale(sale_id, e.message)
        flash[:error] = "Payment failed: #{e.message}"
        redirect_to foodcourt_food_court_path
        return
      end

    end
  end

  def create
    sale_id = params[:sale_id]

    Rails.logger.info("Processing payment for sale ID: #{sale_id}")
    Rails.logger.info("#{params[:response_data]}")

    unless current_login_employee
      render json: { status: false, message: "User not authenticated or employee context missing." }, status: :unauthorized
      return
    end

    qrpayment_service = QrPaymentService.new(sale_id, current_login_employee)
    result = qrpayment_service.process

    PrintReceiptJob.perform_later(current_shop.shop_code, sale_id)

    PaymentGatewayAuditJob.perform_later({
      receipt_no: Sale.find_by(sale_id: sale_id).receipt_no,
      gateway_name: "MMQR",
      endpoint_url: "Response from foodcourt server",
      event_type: "kbz.payment.success",
      request_body: {},
      response_body: params[:response_data].to_json,
      request_method: nil,
      shop_code: Shop.current_shop.shop_code
    })

    if result[:status]
      render json: result, status: :ok
    else
      status_code = result[:message].include?("not found") ? :not_found : :unprocessable_entity
      render json: result, status: status_code
    end
  end

  def req_bill
    sale_data =[]
    customer_id = params[:customer_id]
    if !ShiftSale.current_shift.nil?
      order_id = params[:order_id]
      order = Order.find(order_id)
      booking = order.booking
      if Customer.exists?(customer_id: customer_id)
        booking.orders.update_all(customer_id: customer_id)
      end
      if booking.checkin_at.utc > Time.now.utc && booking.checkout_at.nil?
        @status = false
        @error_message = "Operation failed, Could not request bill!"
      else
        table = DiningFacility.find_by(id: booking.dining_facility_id)

        if booking.sale_id.nil?
          if sale_data = Sale.generate_invoice_from_booking(booking, current_login_employee, current_user, order.source, params[:current_checkin_induties_count])
            # in-duty update
            in_duties = InDuty.where("booking_id=?", booking.id)
            if !in_duties.empty?
              in_duties.each do |in_duty|
                induty = InDuty.find(in_duty.id)
                induty.sale_id = sale_data.sale_id
                induty.out_time = Time.now
                induty.save
              end
            end

            action_by = current_user.name
            type = "REQUEST_BILL"

            remark = "Request bill Receipt No #{sale_data.receipt_no}"
            sale_audit = SaleAudit.record_audit_sale(sale_data.sale_id,remark,action_by,type )

            # Promotion Activation
            Promotion.promo_activate(sale_data)

            #bill channel
            if ENV["SERVER_MODE"] == 'cloud'
              from = request.host
            else
              from = ""
            end

            if ["quick_service", "cashier"].include? order.source
              ActionCable.server.broadcast "bill_channel", table: table, from: from
            end

            unless ["quick_service", "food_court"].include? order.source
              #check checkInOut pdf print
              checkout_time = Lookup.collection_of('checkout_time')
              if !booking.dining_facility_id.nil?
                terminal = DiningFacility.find_by_id(booking.dining_facility_id)
                cashier_terminal = CashierTerminal.find_by_id(terminal.zone_id)

                if (!checkout_time.empty?) && (ENV["SERVER_MODE"] != "cloud") #no print in cloud server
                  unique_code = "CheckInOutPdf"
                  printer = PrintSetting.find_by_unique_code(unique_code)

                  # print when complete click
                  order_queue_printer = Printer::OrderQueuePrinter.new(printer)

                  if !printer.nil?
                    order_queue_printer.print_check_in_out(printer, cashier_terminal, booking, table)
                  end
                end
              end
            end
            @status = true
            sale_id = sale_data.sale_id
          else
            @status = false
            sale_id = nil
          end
        else
          @status = true
          sale_id = booking.sale_id
        end
      end

      respond_to do |format|
        format.json { render :json => { :status => @status, :sale_id => sale_id } }
      end
    else
      respond_to do |format|
        format.json {
          render :json => { :status => false, :error_message => "No Current Open Shift for This Employee" } }
      end
    end
  end

  def test_pay
  end

  def check_payment_status
    receipt_no = params[:receipt_no]

    if receipt_no.blank?
      render json: { status: 'ERROR', message: "Receipt number is missing" }, status: :unprocessable_entity
      return
    end

    @paymethod = PaymentMethodSetting.find_by(payment_method: "MMQR")

    if @paymethod.nil?
      render json: { status: 'CONFIG_ERROR', message: "MMQR payment method not configured" }, status: :ok
      return
    end

    begin
      @merchant = KbzMerchant.new(@paymethod)
      response = @merchant.query_order(merch_order_id: receipt_no)


      if response[:data]['code'] == '0'
        case response[:data]['trade_status']
        when 'PAY_SUCCESS'
          render json: { status: 'PAY_SUCCESS' }
        else
          render json: { status: response[:data]['trade_status'] || 'PENDING' }
        end
      else
        render json: {
          status: 'KBZ_ERROR',
          message: "KBZ Error #{response[:data]['code']}: #{response['msg']}"
        }, status: :ok
      end

    rescue StandardError => e
      render json: {
        status: 'SERVER_ERROR',
        message: "Payment check failed: #{e.message}"
      }, status: :internal_server_error
    end
  end

  def cancel
    # cancel orders and related
    sale_id = params[:sale_id]
    sale = Sale.find_by_sale_id(sale_id)
    order = sale.orders.first
    booking = order.booking

    if !sale_id.present?
      respond_to do |format|
        format.json { render :json => { :status => false, :error_message => "Sale does not exist" } }
      end
    end

    if !order.present?
      respond_to do |format|
        format.json { render :json => { :status => false, :error_message => "Order does not exist" } }
      end
    end

    if !booking.present?
      respond_to do |format|
        format.json { render :json => { :status => false, :error_message => "Booking does not exist" } }
      end
    end

    if order.order_items.present?
      order.order_items.update_all(order_item_status: 'cancelled')
    end

    order.status = 'cancelled'
    order.save

    booking.booking_status = 'cancelled'
    booking.save

    # void order
    remark  = "MMQR payment cancellation"
    order_source = 'foodcourt'
    access_code = 'null'

    # update count for shift sale
    if(sale.sale_status == "completed")
      if sale.shift_sale_id != nil
        shift = ShiftSale.find(sale.shift_sale_id)
        shift.calculate(sale_id, "void")
      end
    else
      # void before sale payment complete
      if sale.shift_sale_id != nil
        shift = ShiftSale.find(sale.shift_sale_id)
        shift.total_void = shift.total_void + sale.grand_total
        shift.save
      end
    end

    if sale.discount_type == "member_discount"
      sale.update_attributes(total_discount: 0)
      sale.compute_by_sale_items(0, nil, order_source)
    end

    sale.rounding_adjustment = 0.0
    sale.payment_status = 'void'
    sale.sale_status = 'void'
    sale.save

    # call close order to qr pay api
    kbz_merchant = KbzMerchant.new(PaymentMethodSetting.find_by(payment_method: 'MMQR'))
    response = kbz_merchant.close_order(merch_order_id: sale.receipt_no)
    Rails.logger.info ">>>>>>>>>>>>>>>>>>>> #{response}"

    PrintReceiptJob.perform_later(current_shop.shop_code, sale.sale_id)

    if table = sale.booking.dining_facility
      unless table.current_bookings.exists?
        table.update_attributes(status: 'available')
      end
    end

    action_by = current_user.name
    if access_code != "null" && current_user.role == "cashier"
      action_by = Employee.find_by_emp_id(access_code).name
    end

    # remark = "Void Sale ID #{sale_id} | Receipt No #{sale.receipt_no} | Receipt No #{sale.receipt_no} | Table ->#{table.name}"
    sale_audit = SaleAudit.record_audit_for_edit(sale.sale_id, current_user.name, action_by,remark, "CANCEL_MMQR_PAYMENT")

    # update complete order items in oqs
    SaleOrder.where("sale_id = '#{sale.sale_id}'").find_each do |sodr|
      AssignedOrderItem.where("order_id = '#{ sodr.order_id }'").find_each do |aoi|
        aoi.delivery_status = 1
        aoi.save
      end
    end

    respond_to do |format|
      format.json { render :json => { status: true, order_id: order.order_id } }
    end
  end

  private

  def cancel_order_and_sale(sale_id, error_message = nil)
    result = { success: false, error: nil }

    sale = Sale.find_by(sale_id: sale_id)
    unless sale
      result[:error] = "Sale not found"
      return result
    end

    order = sale.orders.first
    unless order
      result[:error] = "Order not found"
      return result
    end

    booking = order.booking
    unless booking
      result[:error] = "Booking not found"
      return result
    end

    begin
      Sale.transaction do
        order.order_items.update_all(order_item_status: 'cancelled') if order.order_items.present?

        order.update!(status: 'cancelled')

        booking.update!(booking_status: 'cancelled')

        if sale.shift_sale_id
          shift = ShiftSale.find(sale.shift_sale_id)
          if sale.sale_status == "completed"
            shift.calculate(sale_id, "void")
          else
            shift.update!(total_void: shift.total_void + sale.grand_total)
          end
        end

        if sale.discount_type == "member_discount"
          sale.update!(total_discount: 0)
          sale.compute_by_sale_items(0, nil, 'foodcourt')
        end

        sale.update!(
          rounding_adjustment: 0.0,
          payment_status: 'void',
          sale_status: 'void'
        )

        if table = booking.dining_facility
          table.update!(status: 'available') unless table.current_bookings.exists?
        end

        action_by = current_user.role == "cashier" && params[:access_code].present? ?
                    Employee.find_by(emp_id: params[:access_code])&.name :
                    current_user.name

        remark = error_message ? "Payment failed: #{error_message}" : "Manually cancelled"
        SaleAudit.record_audit_for_edit(
          sale.sale_id,
          current_user.name,
          action_by,
          remark,
          "CANCEL_MMQR_PAYMENT"
        )

        SaleOrder.where(sale_id: sale.sale_id).find_each do |sodr|
          AssignedOrderItem.where(order_id: sodr.order_id).update_all(delivery_status: 1)
        end

        result[:success] = true
      end
    rescue StandardError => e
      result[:error] = "Cancellation failed: #{e.message}"
      Rails.logger.error "Cancellation error: #{e.message}\n#{e.backtrace.join("\n")}"
    end
    result
  end
end
