require 'quickbooks-ruby'
require 'quickbooks-ruby-base'
require_relative 'qbo_rails/error_handler'
class QboRails
  attr_accessor :error, :only_run_once
  attr_reader   :result, :base
  cattr_accessor(:foreign_key) { "qbo_id" }

  include ActiveSupport::Rescuable
  include ErrorHandler
  rescue_from Quickbooks::IntuitRequestException, 
              Quickbooks::InvalidModelException, 
              Quickbooks::AuthorizationFailure, 
              Quickbooks::Forbidden, 
              Quickbooks::MissingRealmError, with: :record_error

  def initialize(account, type = nil)
    @base = Quickbooks::Base.new(account, type) 
  end

  def create(qb_record)
    with_rescued_exception do
      @qb_record = qb_record
      @result = @base.service.create(@qb_record)
      @result
    end
  end

  def create_or_update(record, qb_record)
    with_rescued_exception do
      @record = record
      @qb_record = qb_record
      if id = @record.send(foreign_key)
        prepare_update(id, qb_record)
        @result = @base.service.update(qb_record)
      else
        @result = @base.service.create(@qb_record)
        set_qbo_id
      end
    end
  end

  def delete(record_or_qbo_id)
    with_rescued_exception do
      id = record_or_qbo_id.try(foreign_key) || record_or_qbo_id
      @qb_record = @base.find_by_id(id)
      @result = @base.service.delete(@qb_record)
    end
  end

  protected

  def with_rescued_exception
    yield
  rescue => e
    if @only_run_once
      rescue_with_handler(e) || raise
    else
      unless run_error_handlers(e)
        rescue_with_handler(e) || raise
      end
    end
  end

  def run_error_handlers(e)
    methods.detect do |method|
      if method =~ /^handle_error_/
        send(method, e)
      end
    end
  end

  def qbo_error_params(exception)
    @error = {
      message: exception.message[0..250],
      body: "#{exception}\n\n#{exception.message}\n\n#{exception.backtrace.take(10)}",
      resource_type: @record.try(:class).try(:name),
      resource_id: @record.try(:id),
      request_xml: request_xml(exception)
    }    
  end

  def record_error(exception)
    QboError.create!(qbo_error_params(exception))
  end

  def request_xml(exception)
    if exception.respond_to?(:request_xml)
      exception.request_xml
    elsif defined?(@qb_record)
      @qb_record.to_xml.to_s
    end
  end

  def prepare_update(id, qb_record)
    result = @base.find_by_id(id)
    qb_record.id = result.id
    qb_record.sync_token = result.sync_token
    qb_record
  end

  def set_qbo_id
    @record.update(self.foreign_key => @result.id)
    @record
  end
end