# frozen_string_literal: true

# Orchestrates all Binance sub-importers and upserts a single combined BinanceAccount.
class BinanceItem::Importer
  attr_reader :binance_item, :binance_provider

  def initialize(binance_item, binance_provider:)
    @binance_item = binance_item
    @binance_provider = binance_provider
  end

  def import
    Rails.logger.info "BinanceItem::Importer #{binance_item.id} - starting import"

    spot_result   = BinanceItem::SpotImporter.new(binance_item, provider: binance_provider).import
    margin_result = BinanceItem::MarginImporter.new(binance_item, provider: binance_provider).import
    earn_result   = BinanceItem::EarnImporter.new(binance_item, provider: binance_provider).import

    all_assets = tagged_assets(spot_result) + tagged_assets(margin_result) + tagged_assets(earn_result)

    return { success: true, assets_imported: 0, total_usd: 0 } if all_assets.empty?

    total_usd = calculate_total_usd(all_assets)

    upsert_binance_account(
      all_assets: all_assets,
      total_usd: total_usd,
      spot_raw: spot_result[:raw],
      margin_raw: margin_result[:raw],
      earn_raw: earn_result[:raw]
    )

    binance_item.upsert_binance_snapshot!({
      "spot" => spot_result[:raw],
      "margin" => margin_result[:raw],
      "earn" => earn_result[:raw],
      "imported_at" => Time.current.iso8601
    })

    Rails.logger.info "BinanceItem::Importer #{binance_item.id} - imported #{all_assets.size} assets, total_usd=#{total_usd}"

    { success: true, assets_imported: all_assets.size, total_usd: total_usd }
  end

  private

    def tagged_assets(result)
      result[:assets].map { |a| a.merge(source: result[:source]) }
    end

    def calculate_total_usd(assets)
      assets.sum do |asset|
        quantity = asset[:total].to_d
        next 0 if quantity.zero?

        price = price_for(asset[:symbol])
        quantity * price
      end.round(2)
    end

    def price_for(symbol)
      return 1.0 if BinanceAccount::STABLECOINS.include?(symbol)

      price = binance_provider.get_spot_price("#{symbol}USDT")
      price.to_d
    rescue => e
      Rails.logger.warn "BinanceItem::Importer - could not get price for #{symbol}: #{e.message}"
      0
    end

    def upsert_binance_account(all_assets:, total_usd:, spot_raw:, margin_raw:, earn_raw:)
      ba = binance_item.binance_accounts.find_or_initialize_by(account_type: "combined")

      ba.assign_attributes(
        name: binance_item.institution_name.presence || "Binance",
        currency: "USD",
        current_balance: total_usd,
        institution_metadata: build_institution_metadata(all_assets),
        raw_payload: {
          "spot" => spot_raw,
          "margin" => margin_raw,
          "earn" => earn_raw,
          "assets" => all_assets.map(&:stringify_keys),
          "fetched_at" => Time.current.iso8601
        }
      )

      ba.save!
      ba
    end

    def build_institution_metadata(all_assets)
      %w[spot margin earn].each_with_object({}) do |source, hash|
        source_assets = all_assets.select { |a| a[:source] == source }
        hash[source] = {
          "asset_count" => source_assets.size,
          "assets" => source_assets.map { |a| a[:symbol] }
        }
      end
    end
end
