require 'httparty'

class KbzMerchant
  class PaymentError < StandardError; end

  def initialize(payment_method)
    @payment_method = payment_method
    @url = @payment_method.gateway_url
    json_params = @payment_method.additional_parameters.inspect.undump
    params = JSON.parse(json_params)
    @notify_url = params['notify_url']
    @app_id = params['app_id']
  end

  def create_order(amount:, merch_order_id:, timeout: '120m')
    api_url = "#{@url}/precreate"
    payload = build_create_payload(amount, merch_order_id, timeout)
    response = send_request(payload, api_url)

    # audit
    PaymentGatewayAuditJob.perform_later({
      receipt_no: merch_order_id,
      gateway_name: "MMQR",
      endpoint_url: api_url,
      event_type: "kbz.payment.precreate",
      request_body: payload,
      response_body: response,
      request_method: "POST",
      shop_code: Shop.current_shop.shop_code
    })

    handle_response(response)
  end

  def close_order(merch_order_id:)
    api_url = "#{@url}/closeorder"
    payload = build_close_payload(merch_order_id)
    response = send_request(payload, api_url)

    # audit
    PaymentGatewayAuditJob.perform_later({
      receipt_no: merch_order_id,
      gateway_name: "MMQR",
      endpoint_url: api_url,
      event_type: "kbz.payment.closeorder",
      request_body: payload,
      response_body: response,
      request_method: "POST",
      shop_code: Shop.current_shop.shop_code
    })

    handle_response(response)
  end

  def query_order(merch_order_id:)
    api_url = "#{@url}/queryorder"
    payload = build_query_payload(merch_order_id)
    response = send_request(payload, api_url)
    PaymentGatewayAuditJob.perform_later({
      receipt_no: merch_order_id,
      gateway_name: "MMQR",
      endpoint_url: api_url,
      event_type: "kbz.payment.queryorder",
      request_body: payload,
      response_body: response,
      request_method: "POST",
      shop_code: Shop.current_shop.shop_code
    })
    handle_response(response)
  end

  private

  def build_create_payload(amount, merch_order_id, timeout)
    base_params = {
      method: 'kbz.payment.precreate',
      timestamp: Time.now.utc.to_i.to_s,
      nonce_str: SecureRandom.hex(16),
      notify_url: @notify_url,
      sign_type: 'SHA256',
      version: '1.0',
      biz_content: {
        appid: @app_id,
        merch_code: @payment_method.merchant_account_id,
        merch_order_id: merch_order_id,
        trade_type: 'PAY_BY_QRCODE',
        total_amount: amount.to_s,
        trans_currency: 'MMK',
        timeout_express: timeout
      }.compact
    }

    flattened = flatten_hash(base_params)
    base_params.merge(sign: generate_signature(flattened))
  end

  def build_close_payload(merch_order_id)
    base_params = {
      method: 'kbz.payment.closeorder',
      timestamp: Time.now.utc.to_i.to_s,
      nonce_str: SecureRandom.hex(16),
      sign_type: 'SHA256',
      version: '3.0',
      biz_content: {
        appid: @app_id,
        merch_code: @payment_method.merchant_account_id,
        merch_order_id: merch_order_id
      }.compact
    }

    flattened = flatten_hash(base_params)
    base_params.merge(sign: generate_signature(flattened))
  end

  def build_query_payload(merch_order_id)
    base_params = {
      method: 'kbz.payment.queryorder',
      timestamp: Time.now.utc.to_i.to_s,
      nonce_str: SecureRandom.hex(16),
      sign_type: 'SHA256',
      version: '3.0',
      biz_content: {
        appid: @app_id,
        merch_code: @payment_method.merchant_account_id,
        merch_order_id: merch_order_id
      }.compact
    }

    flattened = flatten_hash(base_params)
    base_params.merge(sign: generate_signature(flattened))
  end

  def flatten_hash(hash, parent_key = nil)
    hash.each_with_object({}) do |(k, v), res|
      key = parent_key ? "#{k}" : k.to_s
      if v.is_a?(Hash)
        res.merge!(flatten_hash(v, key))
      else
        res[key] = v.to_s
      end
    end
  end

  def generate_signature(flattened_params)
    sorted_params = flattened_params.except('sign', 'sign_type').sort
    string_a = sorted_params.map { |k, v| "#{k}=#{v}" }.join('&')
    puts "String a: #{string_a}"
    string_to_sign = "#{string_a}&key=#{@payment_method.auth_token}"
    puts "String to sign: #{string_to_sign}"
    Digest::SHA256.hexdigest(string_to_sign).upcase
  end

  def send_request(payload, url)
    headers = {
      'Content-Type' => 'application/json',
      'User-Agent' => 'KBZPay/1.0'
    }

    puts "Headers: #{headers}"
    puts "Payload: #{payload.to_json}"

    begin
     response = HTTParty.post(
        url,
        headers: headers,
        body: { Request: payload }.to_json,
        timeout: 15
      )

     Rails.logger.info "Response: #{response}"
     JSON.parse(response.body)
    rescue HTTParty::Error => e
      {
        error: true,
        code: 'http_error',
        message: "HTTP error: #{e.message}",
        alert: "Payment service unavailable. Please try again later."
      }
    rescue SocketError => e
      {
        error: true,
        code: 'network_error',
        message: "Network error: #{e.message}",
        alert: "Network connection failed. Please check your internet."
      }
    rescue JSON::ParserError => e
      {
        error: true,
        code: 'invalid_response',
        message: "Invalid response format: #{e.message}",
        alert: "Received invalid payment response. Please contact support."
      }
    rescue StandardError => e
      {
        error: true,
        code: 'unexpected_error',
        message: "Unexpected error: #{e.message}",
        alert: "An unexpected error occurred. Please try again."
      }
    end
  end


  def handle_response(response)
    if response['error']
      {
        status: 'error',
        code: response['code'],
        message: response['message']
      }
    elsif response.dig('Response', 'result') == 'SUCCESS'
      {
        status: 'success',
        data: response['Response']
      }
    else
      error_code = response.dig('Response', 'code')
      error_message = response.dig('Response', 'msg')
      case error_code
      when 'OrderCenter.FAILED_CREATE_ORDER_FOR_DUPLICATED_MERCHANT_ORDER_ID'
        {
          status: 'failed',
          code: error_code,
          message: 'Duplicate order ID detected. Please use a unique reference ID.'
        }
      else
        {
          status: 'failed',
          code: error_code || 'unknown_error',
          message: error_message || 'Payment processing failed'
        }
      end
    end
  end
end
