# frozen_string_literal: true

require "csv"
require "net/http"
require "oj"

require "provider/adapters/adapter"

class Provider
  module Adapters
    # Danmarks Nationalbank. Publishes daily exchange rates for 30 currencies
    # against the Danish krone (DKK) via Statistics Denmark's Statbank API.
    # Rates are quoted as DKK per 100 units of foreign currency.
    class DNB < Adapter
      URL = "https://api.statbank.dk/v1/data"
      CURRENCIES = [
        "EUR",
        "USD",
        "GBP",
        "SEK",
        "NOK",
        "CHF",
        "CAD",
        "JPY",
        "AUD",
        "NZD",
        "PLN",
        "CZK",
        "HUF",
        "HKD",
        "SGD",
        "ZAR",
        "BGN",
        "RON",
        "TRY",
        "KRW",
        "THB",
        "MYR",
        "PHP",
        "IDR",
        "CNY",
        "BRL",
        "MXN",
        "INR",
        "ILS",
        "ISK",
        "ATS",
        "BEF",
        "DEM",
        "ESP",
        "FIM",
        "FRF",
        "GRD",
        "IEP",
        "ITL",
        "NLG",
        "PTE",
        "SIT",
        "ROL",
        "TRL",
      ].freeze

      def fetch(after: nil, upto: nil)
        tid = ">=#{format_date(after)}"
        tid += "<=#{format_date(upto || Date.today)}" if upto

        body = Oj.dump(
          {
            table: "DNVALD",
            format: "BULK",
            lang: "en",
            valuePresentation: "Code",
            variables: [
              { code: "VALUTA", values: CURRENCIES },
              { code: "KURTYP", values: ["KBH"] },
              { code: "Tid", values: [tid] },
            ],
          },
          mode: :compat,
        )

        uri = URI(URL)
        response = Net::HTTP.post(uri, body, "Content-Type" => "application/json")
        parse(response.body)
      end

      def parse(csv)
        csv = csv.encode("UTF-8", "UTF-8", invalid: :replace, undef: :replace)
        csv = csv.sub("\xEF\xBB\xBF", "")
        rows = CSV.parse(csv, col_sep: ";", headers: true)

        rows.filter_map do |row|
          code = row["VALUTA"]
          next unless code&.match?(/\A[A-Z]{3}\z/)

          rate_str = row["INDHOLD"]
          next if rate_str.nil? || rate_str.strip == ".."

          rate = Float(rate_str)
          next if rate.zero?

          tid = row["TID"]
          next unless tid

          date = parse_date(tid)
          { date:, base: code, quote: "DKK", rate: rate / 100.0 }
        end
      end

      private

      def format_date(date)
        date.strftime("%YM%mD%d")
      end

      def parse_date(str)
        Date.new(str[0, 4].to_i, str[5, 2].to_i, str[8, 2].to_i)
      end
    end
  end
end
