# frozen_string_literal: true

module Phony
  class Country
    attr_accessor :codes

    @@international_absolute_format = '+%s%s%s%s%s'
    @@international_relative_format = '00%s%s%s%s%s'
    @@national_format               = '%s%s%s%s'

    @@default_space = ' '
    @@default_local_space = ' '
    @@default_parentheses = false

    # TODO: Doc.
    #
    def initialize *codes
      @codes = codes
    end

    # DSL method.
    #
    # Chain two codes together.
    #
    def |(other)
      self.codes = codes + other.codes
      self
    end

    # Options.
    #
    # TODO Rewrite.
    #
    def with(cc, options = {})
      @cc           = cc

      @invalid_ndcs = options[:invalid_ndcs]

      @format       = options[:format]
      @space        = options[:space]
      @local_space  = options[:local_space]
      @parentheses  = options[:parentheses]
    end

    # A number is split with the code handlers as given in the initializer.
    #
    # Note: If the ndc is nil, it will not return it.
    #
    # @return [Trunk, String (ndc), Array<String> (national pieces)]
    #
    def split(national_number)
      _, trunk, ndc, *rest = internal_split national_number
      [trunk, ndc, *rest]
    end

    #
    #
    # @return [Splitters::Local, Trunk, String (ndc), Array<String> (national pieces)]
    #
    def internal_split(national_number)
      trunk = nil
      @codes.each do |national_splitter|
        new_trunk, ndc, *rest = national_splitter.split national_number
        trunk ||= new_trunk
        national_number = ndc if new_trunk
        return [national_splitter.local_splitter, trunk, ndc, *rest] if rest && !rest.empty?
      end

      # Best effort.
      [nil, trunk, national_number, []]
    end

    # Format the number, given the national part of it.
    #
    def format(national_number, options = {})
      type         = options[:format]       || @format
      space        = options[:spaces]       || @space       || @@default_space
      local_space  = options[:local_spaces] || @local_space || space || @@default_local_space
      parentheses  = options[:parentheses]
      parentheses  = @parentheses || @@default_parentheses if parentheses.nil?
      use_trunk    = options[:trunk]

      trunk, ndc, *local_pieces = split national_number

      local = format_local local_pieces, local_space

      format_cc_ndc trunk, ndc, local, type, space, parentheses, use_trunk
    end

    def format_local(local, local_space)
      if local.empty?
        EMPTY_STRING
      else
        local.compact!
        local.join local_space.to_s
      end
    end

    def format_cc_ndc(trunk, ndc, local, type, space, parentheses, use_trunk)
      # NOTE: We mark NDCs that are of type "none" with false (nil trips plausible?). This would result in false being printed.
      #       Therefore we set NDC to nil when formatting.
      ndc = nil if ndc == false
      case type
      when String
        trunk &&= trunk.format(space, use_trunk)
        type % { trunk: trunk, cc: @cc, ndc: ndc, local: local }
      when nil, :international_absolute, :international, :+
        if ndc
          format_with_ndc(@@international_absolute_format, @cc, format_ndc(ndc, parentheses), local, space)
        else
          format_without_ndc(@@international_absolute_format, @cc, local, space)
        end
      when :international_relative
        if ndc
          format_with_ndc(@@international_relative_format, @cc, format_ndc(ndc, parentheses), local, space)
        else
          format_without_ndc(@@international_relative_format, @cc, local, space)
        end
      when :national
        trunk &&= trunk.format(space, use_trunk)
        if ndc && !ndc.empty?
          @@national_format % [trunk, format_ndc(ndc, parentheses), space, local]
        else
          @@national_format % [trunk, nil, nil, local]
        end
      when :local
        local
      end
    end

    def format_ndc(ndc, parentheses)
      ndc = nil if ndc == false # TODO
      parentheses ? "(#{ndc})" : ndc
    end

    def format_with_ndc(format, cc, ndc, local, space)
      format % [cc, space, ndc, space, local]
    end

    def format_without_ndc(format, cc, local, space)
      format % [cc, space, local, nil, nil]
    end

    # Cleans all non-numeric characters.
    #
    @@basic_cleaning_pattern = /\(0|\D/
    # Clean number of all non-numeric characters and return a copy.
    #
    def clean(number)
      clean! number && number.dup
    end

    # Clean number of all non-numeric characters and return it.
    #
    def clean!(number)
      number.gsub!(@@basic_cleaning_pattern, EMPTY_STRING) || number
    end

    # Removes 0s from partially normalized numbers
    # such as 410443643533.
    #
    # Example:
    #   410443643533 -> 41443643533
    #
    # In some cases it doesn't, like Italy.
    #
    # Note: Options such as CC
    #
    def normalize(national_number, options = {})
      national_number = national_number.dup
      clean! national_number
      @codes.each_with_object national_number do |code, number|
        result = code.normalize number, options
        break result if result
      end
    end

    # Tests for plausibility of this national number.
    #
    def plausible?(rest, hints = {})
      local, _, ndc, *rest = internal_split rest

      # Element based checking.
      #
      # Note: ndc == false means the country has none.
      #
      return false if ndc.nil?
      return false if ndc && ndc.empty?
      return false if @invalid_ndcs && @invalid_ndcs === ndc

      # # A valid range for the rest is 0 or 3+ total digits.
      # #
      # return false if (1..2) === rest_size

      # National destination code plausible?
      #
      ndc_needed = hints[:ndc]
      return false if ndc_needed && !(ndc_needed === ndc)

      # If there is no local part, we can assume it's not a plausible number.
      # (Or, not defined correctly in Phony yet)
      return false unless local

      # Local code specific checks.
      #
      local.plausible? rest, hints
    end

    # Is this national number a vanity number?
    #
    def vanity?(national_number)
      Vanity.vanity? national_number
    end

    def vanity_to_number(vanity_number)
      _, ndc, *rest = split vanity_number
      "#{ndc}#{Vanity.replace(rest.join)}"
    end
  end
end
