# frozen_string_literal: true # CountryHelper - Service for country display utilities # # Provides methods to convert ISO country codes to display names, # generate country flags, and format country data for UI components. class CountryHelper # Convert ISO code to display name def self.display_name(iso_code) return iso_code if iso_code.blank? country = ISO3166::Country[iso_code] country.local_name rescue iso_code end # Convert ISO code to flag emoji def self.flag_emoji(iso_code) return "" if iso_code.blank? || iso_code.length != 2 # Convert each letter to regional indicator symbol (A + 0x1F1E5) iso_code.upcase.codepoints.map { |code| (code + 0x1F1E5).chr }.join rescue "" end # Display name with flag def self.display_with_flag(iso_code) return iso_code if iso_code.blank? "#{display_name(iso_code)} (#{iso_code})" end # Check if ISO code is valid def self.valid_iso_code?(iso_code) return false if iso_code.blank? ISO3166::Country[iso_code].present? rescue false end # Get all countries for select dropdowns # Returns array of [display_name, iso_code] pairs def self.all_for_select ISO3166::Country.all.map do |country| # Try different name sources in order of preference # Use the proper countries gem methods name = country.local_name.presence || country.iso_short_name.presence || country.common_name.presence || country.alpha2 display_name = "#{name} (#{country.alpha2})" [display_name, country.alpha2] end.sort_by { |name, _| name } rescue => e puts "Error in CountryHelper.all_for_select: #{e.message}" puts e.backtrace [] end # Get countries by common regions for quick selection def self.by_region { 'Americas' => [ 'US', 'CA', 'MX', 'BR', 'AR', 'CL', 'CO', 'PE', 'VE' ], 'Europe' => [ 'GB', 'DE', 'FR', 'IT', 'ES', 'NL', 'BE', 'CH', 'AT', 'SE', 'NO', 'DK', 'FI', 'PL', 'CZ', 'HU', 'RO', 'GR', 'PT' ], 'Asia Pacific' => [ 'CN', 'JP', 'KR', 'IN', 'SG', 'AU', 'NZ', 'TH', 'MY', 'ID', 'PH', 'VN', 'HK', 'TW' ], 'Middle East & Africa' => [ 'ZA', 'EG', 'NG', 'KE', 'SA', 'AE', 'IL', 'TR', 'IR' ] } rescue {} end # Get countries for specific region with display names def self.countries_for_region(region_name) country_codes = by_region[region_name] || [] country_codes.map do |code| { code: code, name: display_name(code), display: display_with_flag(code) } end end # Format multiple country targets for display def self.format_targets(targets) return [] if targets.blank? targets.map do |target| { code: target, name: display_name(target), display: display_with_flag(target) } end end # Get popular countries for quick blocking (common threat sources) def self.popular_for_blocking [ { code: 'CN', name: 'China', display: '🇨🇳 China', reason: 'High bot/scanner activity' }, { code: 'RU', name: 'Russia', display: '🇷🇺 Russia', reason: 'State-sponsored attacks' }, { code: 'IN', name: 'India', display: '🇮🇳 India', reason: 'High spam volume' }, { code: 'BR', name: 'Brazil', display: '🇧🇷 Brazil', reason: 'Scanner activity' }, { code: 'IR', name: 'Iran', display: '🇮🇷 Iran', reason: 'Attacks on critical infrastructure' }, { code: 'KP', name: 'North Korea', display: '🇰🇵 North Korea', reason: 'State-sponsored hacking' } ] end # Search countries by name or code def self.search(query) return [] if query.blank? query = query.downcase ISO3166::Country.all.select do |country| country.alpha2.downcase.include?(query) || country.local_name.downcase.include?(query) end.first(20).map { |c| [display_with_flag(c.alpha2), c.alpha2] } rescue [] end # Country statistics for analytics def self.usage_statistics(country_codes) return {} if country_codes.blank? stats = {} country_codes.each do |code| stats[code] = { name: display_name(code), flag: flag_emoji(code), display: display_with_flag(code) } end stats end end