# frozen_string_literal: true

require "json"
require "net/http"

require "provider/adapters/adapter"

class Provider
  module Adapters
    # Central Bank of Kenya daily exchange rates in KES.
    # Fetches from two WPDataTables endpoints: table 32 (2003-2024) and table 193 (2024+).
    class CBK < Adapter
      BASE_URL = "https://www.centralbank.go.ke/wp-admin/admin-ajax.php"
      CURRENCY_MAP = {
        "US DOLLAR" => "USD",
        "STG POUND" => "GBP",
        "EURO" => "EUR",
        "JAPANESE YEN" => "JPY",
        "JPY" => "JPY",
        "SWISS FRANC" => "CHF",
        "S FRANC" => "CHF",
        "CAN DOLLAR" => "CAD",
        "CAN $" => "CAD",
        "AUSTRALIAN DOLLAR" => "AUD",
        "AUSTRALIAN $" => "AUD",
        "INDIAN RUPEE" => "INR",
        "IND RUPEE" => "INR",
        "SWEDISH KRONA" => "SEK",
        "SW KRONER" => "SEK",
        "NORWEGIAN KRONE" => "NOK",
        "NOR KRONER" => "NOK",
        "DAN KRONER" => "DKK",
        "CHINESE YUAN" => "CNY",
        "S. AFRICAN RAND" => "ZAR",
        "SA RAND" => "ZAR",
        "AE DIRHAM" => "AED",
        "UAE DIRHAM" => "AED",
        "HONGKONG DOLLAR" => "HKD",
        "SINGAPORE DOLLAR" => "SGD",
        "SAUDI RIYAL" => "SAR",
        "USHS" => "UGX",
        "TSHS" => "TZS",
        "RWF" => "RWF",
        "BIF" => "BIF",
      }.freeze

      def fetch(after: nil, upto: nil)
        dataset = []

        if after.nil? || after < Date.new(2024, 1, 5)
          dataset.concat(fetch_table(32))
        end

        dataset.concat(fetch_table(193))

        dataset = dataset.select { |r| r[:date] >= after } if after
        dataset = dataset.select { |r| r[:date] <= upto } if upto

        dataset
      end

      def parse(json)
        data = json.is_a?(String) ? JSON.parse(json) : json
        rows = data["data"]
        return [] unless rows.is_a?(Array)

        rows.filter_map do |row|
          parse_row(row)
        end
      end

      private

      def fetch_table(table_id)
        uri = URI("#{BASE_URL}?action=get_wdtable&table_id=#{table_id}")
        request = Net::HTTP::Post.new(uri)
        request.set_form_data("draw" => "1", "start" => "0", "length" => "-1")

        response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
          http.request(request)
        end

        parse(response.body)
      end

      def parse_row(row)
        date_str, currency_name, *rest = row
        return unless date_str && currency_name

        date = Date.strptime(date_str.strip, "%d/%m/%Y")
        iso, cross_rate = resolve_currency(currency_name.strip)
        return unless iso

        units = parse_units(currency_name)
        rate_value = Float(rest[0])
        return if rate_value.zero?

        rate_value /= units if units > 1
        rate_value = 1.0 / rate_value if cross_rate

        { date:, base: iso, quote: "KES", rate: rate_value }
      end

      def resolve_currency(name)
        upname = name.upcase

        # Check for East African cross-rate patterns like "KES / USHS" or "KEN SHILLING / USHS"
        if upname.match?(%r{KE[SN][\s/]|KEN SHILLING\s*/})
          suffix = upname.split("/").last.strip
          iso = CURRENCY_MAP[suffix]
          return [iso, true] if iso
        end

        # Strip unit markers like "(100)" before matching
        clean = upname.sub(/\s*\(\d+\)\s*$/, "")
        iso = CURRENCY_MAP[clean] || CURRENCY_MAP.find { |k, _| clean.include?(k) }&.last
        [iso, false]
      end

      def parse_units(name)
        match = name.match(/\((\d+)\)\s*$/)
        match ? Integer(match[1]) : 1
      end
    end
  end
end
