require 'set'
require 'erb'
require 'digest'
require 'securerandom'

module CocoaPodsKeys
  class KeyMaster
    attr_accessor :name, :interface, :implementation

    def initialize(keyring, time = Time.now)
      @time = time
      @keys = keyring.camel_cased_keys
      @name = keyring.code_name + 'Keys'
      if /^\d/ =~ @name
        @name = '_' + @name
      end

      @used_indexes = Set.new
      @indexed_keys = {}
      @data = generate_data
      @interface = generate_interface
      @implementation = generate_implementation
    end

    def generate_data
      # guard method; raises error if something in application keychain
      # is nil. provides for better error message.
      verify_keychain_integrity

      # Generate a base64 hash string that is ~25 times the length of all keys
      @data_length = @keys.values.map(&:length).reduce(0, :+) * (20 + rand(10))
      data = SecureRandom.base64(@data_length)
      data += '\\"'
      @data_length = data.length

      # Swap the characters within the hashed string with the characters from
      # the keyring values. Then store that index in a index-ed copy of the values.

      @keys.each do |key, value|
        @indexed_keys[key] = []

        value.chars.each_with_index do |char, char_index|
          loop do
            if char == '"'
              index = data.delete('\\').length - 1
              @indexed_keys[key][char_index] = index
              break
            else
              index = SecureRandom.random_number data.length
              unless @used_indexes.include?(index)
                data[index] = char

                @used_indexes << index
                @indexed_keys[key][char_index] = index
                break
              end
            end
          end
        end
      end

      data
    end

    def generate_interface
      render_erb('Keys.h.erb')
    end

    def generate_implementation
      render_erb('Keys.m.erb')
    end

    def verify_keychain_integrity
      if @keys.any? { |k, v| k.nil? || v.nil? }
        raise 'A key/value pair in the application keychain is nil.'
      end
    end

    private

    def render_erb(erb_template)
      erb = (Pathname(__dir__).parent + 'templates' + erb_template).read
      ERB.new(erb, trim_mode: '-').result(binding)
    end

    def key_data_arrays
      Hash[@indexed_keys.map { |key, value| [key, value.map { |i| '[' + name + "Data characterAtIndex:#{i}]" }.join(', ')] }]
    end
  end
end
