# frozen_string_literal: true

require "net/http"
require "ox"

require "provider/adapters/adapter"

class Provider
  module Adapters
    # Central Bank of Armenia. Publishes daily rates for ~30 currencies against AMD.
    class CBA < Adapter
      URL = URI("https://api.cba.am/exchangerates.asmx")
      CHUNK_SIZE = 365
      TROY_OUNCE_GRAMS = 31.1035
      PRECIOUS_METALS = ["XAU", "XAG"].freeze

      def fetch(after: nil, upto: nil)
        end_date = upto || Date.today
        iso_codes = current_currency_codes
        records = []
        chunk_start = after

        while chunk_start <= end_date
          chunk_end = [chunk_start + CHUNK_SIZE - 1, end_date].min
          records.concat(range(chunk_start, chunk_end, iso_codes))
          chunk_start = chunk_end + 1
        end

        records
      end

      private

      def current_currency_codes
        response = request("ExchangeRatesLatest", <<~XML)
          <ExchangeRatesLatest xmlns="http://www.cba.am/" />
        XML

        result = response.locate("soap:Envelope/soap:Body/ExchangeRatesLatestResponse/ExchangeRatesLatestResult").first
        return "" unless result

        result.locate("Rates/ExchangeRate").filter_map do |node|
          node.locate("ISO").first&.text
        end.join(",")
      end

      def range(start_date, end_date, iso_codes)
        response = request("ExchangeRatesByDateRangeByISO", <<~XML)
          <ExchangeRatesByDateRangeByISO xmlns="http://www.cba.am/">
            <ISOCodes>#{iso_codes}</ISOCodes>
            <DateFrom>#{start_date}</DateFrom>
            <DateTo>#{end_date}</DateTo>
          </ExchangeRatesByDateRangeByISO>
        XML

        response
          .locate("soap:Envelope/soap:Body/ExchangeRatesByDateRangeByISOResponse/ExchangeRatesByDateRangeByISOResult/diffgr:diffgram/DocumentElement/ExchangeRatesByRange")
          .filter_map do |row|
            iso = row.locate("ISO").first&.text
            next unless iso

            { date: Date.parse(row.locate("RateDate").first.text), base: iso, quote: "AMD", rate: extract_rate(row) }
          end
      end

      def request(action, payload)
        xml = <<~XML
          <?xml version="1.0" encoding="utf-8"?>
          <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                         xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
            <soap:Body>
              #{payload.strip}
            </soap:Body>
          </soap:Envelope>
        XML

        Ox.load(
          Net::HTTP.post(
            URL,
            xml,
            {
              "Content-Type" => "text/xml; charset=utf-8",
              "SOAPAction" => "\"http://www.cba.am/#{action}\"",
            },
          ).body,
        )
      end

      def extract_rate(node)
        iso = node.locate("ISO").first&.text
        amount = Integer(node.locate("Amount").first.text)
        rate = Float(node.locate("Rate").first.text)
        rate *= TROY_OUNCE_GRAMS if PRECIOUS_METALS.include?(iso)
        rate / amount
      end
    end
  end
end
