# frozen_string_literal: true

require 'spec_helper'
require 'benchmark'

describe ISO3166::Data do
  it 'responds to call' do
    data = ISO3166::Data.new('US').call
    expect(data['translated_names']).to be_a Array
  end

  it 'can load selective locales' do
    # ISO3166::Data.update_cache
    ISO3166.configuration.locales = %i[es de en]
    data = ISO3166::Data.new('US').call
    expect(data['translated_names'].size).to eq 3
  end

  it 'can load selective locales and reload efficiently' do
    ISO3166.configuration.locales = %i[es de en]
    data = ISO3166::Data.new('US').call
    expect(data['translations']).to eq(de: 'Vereinigte Staaten', es: 'Estados Unidos', en: 'United States')
    expect(data['translated_names'].sort).to eq ['Vereinigte Staaten', 'Estados Unidos', 'United States'].sort
    ISO3166.configuration.locales = [:en]
    data = ISO3166::Data.new('US').call
    expect(data['translated_names'].size).to eq 1
  end

  it 'only loads subdivision translations for the configured locales' do
    ISO3166.configuration.locales = %i[en]
    ISO3166::Data.reset
    subdivisions = ISO3166::Data.subdivisions('US')
    expect(subdivisions.values.first['translations'].keys).to eq(%i[en])
    ISO3166.configuration.locales = %i[es de en]
    ISO3166::Data.reset
    subdivisions = ISO3166::Data.subdivisions('US')
    expect(subdivisions.values.first['translations'].keys).to eq(%i[es de en])
  end

  describe '#codes' do
    it 'returns an array' do
      data = ISO3166::Data.codes
      expect(data).to be_a Array
      expect(data.size).to eq 249
    end
  end

  it 'locales will load prior to return results' do
    ISO3166.configuration.locales = %i[es de en]
    ISO3166::Data.update_cache

    ISO3166::Data.update_cache

    ISO3166.configure do |config|
      config.locales = %i[af am ar as az be bg bn br bs ca cs cy da de dz el en
                          eo es et eu fa fi fo fr ga gl gu he hi hr hu hy ia id
                          is it ja ka kk km kn ko ku lt lv mi mk ml mn mr ms mt
                          nb ne nl nn oc or pa pl ps pt ro ru rw si sk sl so sq
                          sr sv sw ta te th ti tk tl tr tt ug uk ve vi wa wo xh
                          zh zu]
    end

    ISO3166::Data.update_cache

    expect(ISO3166::Country.new('DE').translations.size).to eq 92

    expect(ISO3166.configuration.loaded_locales.size).to eq 92
  end

  it 'locales will load prior and be cached' do
    ISO3166.reset
    ISO3166.configuration.locales = %i[es de en]
    expect(ISO3166::Data.send(:locales_to_load)).to eql(%i[es de en])
    ISO3166::Data.update_cache
    ISO3166.configuration.locales = %i[es de en]
    expect(ISO3166::Data.send(:locales_to_load)).to eql([])
  end

  it 'locales will load prior and be cached' do
    ISO3166.reset
    ISO3166.configuration.locales = %i[es de en]
    expect(ISO3166::Data.send(:locales_to_remove)).to eql([])
    expect(ISO3166::Country.new('DE').translation('de')).to eq 'Deutschland'
    ISO3166::Data.update_cache
    ISO3166.configuration.locales = %i[es en]
    expect(ISO3166::Data.send(:locales_to_remove)).to eql([:de])
    expect(ISO3166::Country.new('DE').translation('de')).to eq nil
  end

  describe '#load_cache' do
    it 'will return an empty hash for an unsupported locale' do
      file_array = %w[locales unsupported.json]
      expect(ISO3166::Data.send(:load_cache, file_array)).to eql({})
    end

    it 'will return json for a supported locale' do
      file_array = %w[locales en.json]
      expect(ISO3166::Data.send(:load_cache, file_array)).not_to be_empty
    end
  end

  describe 'hotloading existing data' do
    before do
      ISO3166::Data.register(
        alpha2: 'TW',
        iso_short_name: 'NEW Taiwan',
        subdivisions: {
          CHA: { name: 'New Changhua' },
          CYI: { name: 'New Municipality' }
        },
        translations: {
          'en' => 'NEW Taiwan',
          'de' => 'NEW Taiwan'
        }
      )
    end

    subject { ISO3166::Country.new('TW') }

    it 'can be done' do
      data = ISO3166::Data.new('TW').call
      ISO3166.configuration.locales = %i[es de de]
      expect(data['iso_short_name']).to eq 'NEW Taiwan'
      expect(subject.iso_short_name).to eq 'NEW Taiwan'
      expect(subject.translations).to eq(en: 'NEW Taiwan',
                                         de: 'NEW Taiwan')
      expect(subject.subdivisions.keys).to eq(%w[CHA CYI])
      expect(subject.subdivisions.values.map(&:name)).to eq(['New Changhua', 'New Municipality'])
    end
  end

  describe 'hotloading existing data with symbol keyed translations' do
    before do
      ISO3166::Data.register(
        alpha2: 'TW',
        iso_short_name: 'NEW Taiwan',
        subdivisions: {
          CHA: { name: 'New Changhua' },
          CYI: { name: 'New Municipality' }
        },
        translations: {
          en: 'NEW Taiwan',
          de: 'NEW Taiwan'
        }
      )
    end

    subject { ISO3166::Country.new('TW') }

    it 'can be done ' do
      data = ISO3166::Data.new('TW').call
      ISO3166.configuration.locales = %i[es de de]
      expect(data['iso_short_name']).to eq 'NEW Taiwan'
      expect(subject.iso_short_name).to eq 'NEW Taiwan'
      expect(subject.translations).to eq(en: 'NEW Taiwan',
                                         de: 'NEW Taiwan')
      expect(subject.subdivisions.keys).to eq(%w[CHA CYI])
      expect(subject.subdivisions.values.map(&:name)).to eq(['New Changhua', 'New Municipality'])
    end

    after do
      ISO3166.reset
    end
  end

  describe 'hotloading data' do
    before do
      ISO3166::Data.register(
        alpha2: 'LOL',
        iso_short_name: 'Happy Country',
        subdivisions: {
          LOL1: { name: 'Happy sub1' },
          LOL2: { name: 'Happy sub2' }
        },
        translations: {
          'en' => 'Happy Country',
          'de' => 'glückliches Land'
        }
      )
    end

    subject { ISO3166::Country.new('LOL') }

    it 'can be done' do
      data = ISO3166::Data.new('LOL').call
      expect(data['iso_short_name']).to eq 'Happy Country'
      expect(subject.iso_short_name).to eq 'Happy Country'
      expect(subject.subdivisions.keys).to eq(%w[LOL1 LOL2])
      expect(subject.subdivisions.values.map(&:name)).to eq(['Happy sub1', 'Happy sub2'])
    end

    it 'detect a stale cache' do
      ISO3166::Data.register(alpha2: 'SAD', iso_short_name: 'Sad Country')
      data = ISO3166::Data.new('SAD').call
      expect(data['iso_short_name']).to eq 'Sad Country'
      expect(ISO3166::Country.new('SAD').iso_short_name).to eq 'Sad Country'
      ISO3166::Data.unregister('SAD')
    end

    it 'will not override custom translations' do
      data = ISO3166::Data.new('LOL').call
      expect(data['translations']).to eq(en: 'Happy Country',
                                         de: 'glückliches Land')
      expect(subject.translations).to eq(en: 'Happy Country',
                                         de: 'glückliches Land')
    end

    it 'leaves remain countries intact after a hotload' do
      data = ISO3166::Data.new('US').call
      expect(data).to include('subregion')
    end

    it 'can be undone' do
      ISO3166::Data.unregister('lol')
      data = ISO3166::Data.new('LOL').call
      expect(data).to eq nil
    end

    after do
      ISO3166.reset
    end
  end

  describe 'data checks' do
    context 'subdivision YAML files' do
      it 'has a non-blank code for all subdivisions' do
        Dir['lib/countries/data/subdivisions/*.yaml'].each do |file|
          data = YAML.load_file(file)
          expect(data.values.none? { |s| s['code'].nil? }).to be_truthy, "empty subdivision code in #{file}"
        end
      end

      it 'has a non-blank, lowercase and snake_case type for all subdivisions' do
        Dir['lib/countries/data/subdivisions/*.yaml'].each do |file|
          data = YAML.load_file(file)
          no_type = data.select { |_k, v| v['type'].nil? }
          expect(no_type).to be_empty, "empty subdivision type in #{file} - #{no_type.keys}"
          uppercase = data.select { |_k, v| v['type'] =~ /[A-Z]/ }
          expect(uppercase).to be_empty, "uppercase characters in subdivision type in #{file} - #{uppercase.keys}"
          spaces = data.select { |_k, v| v['type'] =~ /\s/ }
          expect(spaces).to be_empty, "whitespace characters in subdivision type in #{file} - #{spaces.keys}"
        end
      end

      it 'has a non-blank name for all subdivisions' do
        Dir['lib/countries/data/subdivisions/*.yaml'].each do |file|
          data = YAML.load_file(file)
          expect(data.values.none? { |s| s['name'].nil? }).to be_truthy, "empty subdivision name in #{file}"
        end
      end
    end

    context 'cached country subdivision data' do
      before do
        ISO3166.reset
      end

      it 'has a non-blank code for all subdivisions' do
        ISO3166::Country.all.each do |country|
          expect(country.subdivisions.values.none? do |s|
                   s['code'].nil?
                 end).to be_truthy, "empty subdivision code in #{country}"
        end
      end

      it 'has a non-blank name for all subdivisions' do
        ISO3166::Country.all.each do |country|
          expect(country.subdivisions.values.none? do |s|
                   s['name'].nil?
                 end).to be_truthy, "empty subdivision name in #{country}"
        end
      end
    end

    context 'names in Data' do
      it 'should be unique (to allow .find_by_any_name work properly)' do
        names = ISO3166::Data.cache.map do |_k, v|
          [v['iso_short_name'], v['unofficial_names']].flatten.uniq
        end.flatten

        expect(names.size).to eq(names.uniq.size)
      end
    end
  end
end
