# -*- encoding : utf-8 -*-

require 'phrase/delegate/i18n'

class Phrase::Backend::PhraseService
  
  attr_accessor :api_client, :blacklisted_keys
    
  def initialize(args = {})
    self
  end
  
  def translate(*args)
    if to_be_translated_without_phrase?(args)
      I18n.translate_without_phrase(*args)
    else
      phrase_delegate_for(args)
    end
  end

protected
  def to_be_translated_without_phrase?(args)
    Phrase.disabled? or has_been_given_blacklisted_key?(args) or has_been_given_ignored_key?(args) or has_been_forced_to_resolve_with_phrase?(args)
  end

  def has_been_given_blacklisted_key?(args)
    key = given_key_from_args(args)
    has_blacklist_entry_for_key?(key)
  end

  def has_been_given_ignored_key?(args)
    key = given_key_from_args(args)
    key_is_ignored?(key)
  end

  def has_been_forced_to_resolve_with_phrase?(args)
    (args.last.is_a?(Hash) and args.last[:resolve] == false)
  end

  def given_key_from_args(args)
    extract_normalized_key_from_args(args)
  end

  def has_blacklist_entry_for_key?(key)
    blacklisted_keys.each do |blacklisted_key|
      return true if present?(key.to_s[/\A#{blacklisted_key.gsub("*", ".*")}\Z/])
    end
    false
  end

  def key_is_ignored?(key)
    Phrase.ignored_keys.each do |ignored_key|
      return true if present?(key.to_s[/\A#{ignored_key.gsub("*", ".*")}\Z/])
    end
    false
  end
  
  def blacklisted_keys
    @blacklisted_keys ||= api_client.fetch_blacklisted_keys
  end
  
  def api_client
    @api_client ||= Phrase::Api::Client.new(Phrase.auth_token)
  end
  
  def phrase_delegate_for(args)
    key = given_key_from_args(args)
    return nil unless present?(key)
    options = args[1].nil? ? {} : args[1]
    Phrase::Delegate::I18n.new(key, options)
  end

  def extract_normalized_key_from_args(args)
    transformed_args = transform_args(args)
    normalized_key(transformed_args)
  end

  def transform_args(args)
    duped_args = args.map { |item| (item.is_a?(Symbol) or item.nil?) ? item : item.dup }
    transform_args_based_on_caller(duped_args)
  end

  def normalized_key(duped_args)
    splitted_args = split_args(duped_args)
    key = I18n::Backend::Flatten.normalize_flat_keys(*splitted_args)
    key.gsub!("..", ".")
    key
  end
  
  def split_args(args)
    options = options_from_args(args) 
    key ||= args.shift
    locale = options.delete(:locale) || I18n.locale
    return [locale, key, options[:scope], nil]
  end

  def options_from_args(args)
    args.last.is_a?(Hash) ? args.pop : {}
  end

  def transform_args_based_on_caller(args)
    translation_caller = identify_caller
    
    if translation_caller and args.first =~ /^\./
      options = options_from_args(args)

      if not present?(options[:scope]) and present?(translation_caller)
        options[:scope] = translation_caller
      end

      args.push(options)
      parts = args.first.to_s.split(".").select { |e| not blank?(e) }
      args[0] = parts[0] if parts.size == 1
    end

    args
  end

  def identify_caller
    translation_caller = nil
    send(:caller)[0..6].each do |intermediate_caller|
      translation_caller = calling_template(intermediate_caller) unless translation_caller
    end
    
    if present?(translation_caller)
      find_lookup_scope(translation_caller)
    else
      nil
    end
  end

  def calling_template(string)
    string.match(/(views)(\/.+)(?>:[0-9]+:in)/)
  end

  def blank?(str)
    raise "blank?(str) can only be given a String or nil" unless str.is_a?(String) or str.nil?
    str.nil? or str == '' 
  end

  def present?(str)
    raise "present?(str) can only be given a String or nil" unless str.is_a?(String) or str.nil?
    not blank?(str)
  end

  def find_lookup_scope(caller)
    split_path = caller[2][1..-1].split(".")[0].split("/")

    template_or_partial = remove_underscore_form_partial(split_path[-1])
    split_path[-1] = template_or_partial

    split_path.map!(&:to_sym)
  end

  def remove_underscore_form_partial(template_or_partial)
    if template_or_partial.to_s[0,1] == "_"
      template_or_partial.to_s[1..-1] 
    else
      template_or_partial.to_s
    end
  end
end