class TradeBuilder
  include ActiveModel::Model

  attr_accessor :account, :date, :amount, :currency, :qty,
                :price, :ticker, :manual_ticker, :type, :transfer_account_id

  attr_reader :buildable

  def initialize(attributes = {})
    super
    @buildable = set_buildable
  end

  def save
    buildable.save
  end

  def lock_saved_attributes!
    if buildable.is_a?(Transfer)
      buildable.inflow_transaction.entry.lock_saved_attributes!
      buildable.outflow_transaction.entry.lock_saved_attributes!
    else
      buildable.lock_saved_attributes!
    end
  end

  def entryable
    return nil if buildable.is_a?(Transfer)

    buildable.entryable
  end

  def errors
    buildable.errors
  end

  def sync_account_later
    buildable.sync_account_later
  end

  private
    def set_buildable
      case type
      when "buy", "sell"
        build_trade
      when "deposit", "withdrawal"
        build_transfer
      when "interest"
        build_interest
      else
        raise "Unknown trade type: #{type}"
      end
    end

    def build_trade
      prefix = type == "sell" ? "Sell " : "Buy "
      trade_name = prefix + "#{qty.to_i.abs} shares of #{security.ticker}"

      account.entries.new(
        name: trade_name,
        date: date,
        amount: signed_amount,
        currency: currency,
        entryable: Trade.new(
          qty: signed_qty,
          price: price,
          currency: currency,
          security: security
        )
      )
    end

    def build_transfer
      transfer_account = family.accounts.find(transfer_account_id) if transfer_account_id.present?

      if transfer_account
        from_account = type == "withdrawal" ? account : transfer_account
        to_account = type == "withdrawal" ? transfer_account : account

        Transfer.from_accounts(
          from_account: from_account,
          to_account: to_account,
          date: date,
          amount: signed_amount
        )
      else
        account.entries.build(
          name: signed_amount < 0 ? "Deposit to #{account.name}" : "Withdrawal from #{account.name}",
          date: date,
          amount: signed_amount,
          currency: currency,
          entryable: Transaction.new
        )
      end
    end

    def build_interest
      account.entries.build(
        name: "Interest payment",
        date: date,
        amount: signed_amount,
        currency: currency,
        entryable: Transaction.new
      )
    end

    def signed_qty
      return nil unless type.in?([ "buy", "sell" ])

      type == "sell" ? -qty.to_d : qty.to_d
    end

    def signed_amount
      case type
      when "buy", "sell"
        signed_qty * price.to_d
      when "deposit", "withdrawal"
        type == "deposit" ? -amount.to_d : amount.to_d
      when "interest"
        amount.to_d * -1
      end
    end

    def family
      account.family
    end

    # Users can either look up a ticker from our provider (Synth) or enter a manual, "offline" ticker (that we won't fetch prices for)
    def security
      ticker_symbol, exchange_operating_mic = ticker.present? ? ticker.split("|") : [ manual_ticker, nil ]

      Security::Resolver.new(
        ticker_symbol,
        exchange_operating_mic: exchange_operating_mic
      ).resolve
    end
end
