require 'PushRadar/version'
require 'PushRadar/Targeting'
require 'PushRadar/APIClient'
require 'json'

module PushRadar

  # A realtime push notifications API service for the web, featuring advanced targeting
  class Radar

    # ------------------------------
    # Initialisation & configuration
    # ------------------------------

    # Creates a new instance of PushRadar with the specified API secret
    def initialize(api_secret)

      # Initialize data
      @data = {}

      # Reset the targeting options
      reset_targeting

      # Check that the API secret is a string
      unless api_secret.is_a?(String)
        raise 'The API secret must be a string value.'
      end

      # Trim the API secret
      api_secret.strip!

      # If we are running unit tests, set the unit test mode flag and skip the PushRadar API client initialisation
      @is_unit_test_mode = (api_secret == 'test-secret')

      # Check that the API secret starts with sk_
      unless api_secret.start_with?('sk_')
        raise 'API secret invalid. You can view your PushRadar API secret at https://dashboard.pushradar.com/api'
      end

      # Make a new instance of a PushRadar API client with the specified API secret
      @api_client = APIClient.new(api_secret)

    end

    # ------------------------------
    # Browser targeting
    # ------------------------------

    # Targets the notification to clients currently using the given browser
    def target_browser(browser)

      # Add the browser to the list of target browsers
      @targeting.target_browser browser

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently using any of the given browsers
    def target_browsers(*browsers)

      # Target each browser
      browsers.each {|x|
        target_browser x
      }

      # Allow method chaining
      self

    end

    # ------------------------------
    # Country targeting
    # ------------------------------

    # Targets the notification to clients currently located in the given country
    def target_country(country_code)

      # Add the country to the list of target countries
      @targeting.target_country country_code

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in any of the given countries
    def target_countries(*country_codes)

      # Target each country
      country_codes.each {|x|
        target_country x
      }

      # Allow method chaining
      self

    end

    # ------------------------------
    # Continent targeting
    # ------------------------------

    # Targets the notification to clients currently located in Asia
    def target_asia

      # Target the continent
      @targeting.target_continent('AS')

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in Africa
    def target_africa

      # Target the continent
      @targeting.target_continent('AF')

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in Antarctica
    def target_antarctica

      # Target the continent
      @targeting.target_continent('AN')

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in Europe
    def target_europe

      # Target the continent
      @targeting.target_continent('EU')

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in North America
    def target_north_america

      # Target the continent
      @targeting.target_continent('NA')

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in South America
    def target_south_america

      # Target the continent
      @targeting.target_continent('SA')

      # Allow method chaining
      self

    end

    # Targets the notification to clients currently located in Oceania
    def target_oceania

      # Target the continent
      @targeting.target_continent('OC')

      # Allow method chaining
      self

    end

    # ------------------------------
    # IP address targeting
    # ------------------------------

    # Targets the notification to clients with the given IP address
    def target_ip(ip_address)

      # Add the IP address to the list of target IP addresses
      @targeting.target_ip ip_address

      # Allow method chaining
      self

    end

    # Targets the notification to clients with any of the given IP addresses
    def target_ips(*ip_addresses)

      # Target each IP address
      ip_addresses.each {|x|
        target_ip x
      }

      # Allow method chaining
      self

    end

    # ------------------------------
    # Action targeting
    # ------------------------------

    # Targets the notification to clients who have taken the given action
    def target_action(action_identifier)

      # Add the action to the list of target actions
      @targeting.target_action action_identifier

      # Allow method chaining
      self

    end

    # Targets the notification to clients who have taken any of the given actions
    def target_actions(*action_identifiers)

      # Target each action
      action_identifiers.each {|x|
        target_action x
      }

      # Allow method chaining
      self

    end

    # Targets the notification to clients who have not taken the given action
    def target_not_action(action_identifier)

      # Add the action to the list of target "not" actions
      @targeting.target_not_action action_identifier

      # Allow method chaining
      self

    end

    # Targets the notification to clients who have not taken any of the given actions
    def target_not_actions(*action_identifiers)

      # Target each action
      action_identifiers.each {|x|
        target_not_action x
      }

      # Allow method chaining
      self

    end

    # ------------------------------
    # User targeting
    # ------------------------------

    # Targets the notification to a specific user (identifier by their user ID)
    def target_user(user_id)

      # Target the user
      @targeting.target_user user_id

      # Allow method chaining
      self

    end

    # Targets the notification to specific users (identifier by their user IDs)
    def target_users(user_ids)

      # Target the user IDs
      user_ids.each {|x|
        target_user x
      }

      # Allow method chaining
      self

    end

    # ------------------------------
    # Adding data
    # ------------------------------

    # Adds a data item to the list of data items
    def add_data_item(key, value)

      # Make sure the key is not empty
      if key == ''
        raise 'The key provided cannot be empty.'
      end

      # Add the data item
      unless @data.keys.include?(key)
        @data[key] = value
      end

      # Allow method chaining
      self

    end

    # Adds multiple data items to the list of data items
    def add_data_items(data = {})

      # Add the data items
      data.keys.each {|x|
        add_data_item x, data[x]
      }

      # Allow method chaining
      self

    end

    # ------------------------------
    # Broadcast method
    # ------------------------------

    # Broadcasts data on the channel specified
    def broadcast(channel, data = {})

      # If we are running unit tests, throw an exception
      if @is_unit_test_mode
        raise 'Unit testing of the broadcast() method is not supported.'
      end

      # Trim the channel name
      channel.strip!

      # Check whether data has been provided
      if data.length == 0 && @data.length == 0
        raise 'There is no data to broadcast.'
      end

      # Check whether the channel name contains spaces
      if channel.include? ' '
        raise "The channel name cannot contain spaces. By convention, channel names are alphanumerical and lowercase, with optional dashes (e.g. 'test-channel')."
      end

      # Use the stored data if the data parameter is empty
      if data.length == 0
        data = @data
      end

      # Initialize the hash of data to send to the server
      data_to_send = {
          channel: channel,
          notification: data.to_json,
          target_user_ids: @targeting.instance_variable_get('@target_user_ids').to_json,
          target_actions: @targeting.instance_variable_get('@target_actions').to_json,
          target_not_actions: @targeting.instance_variable_get('@target_not_actions').to_json,
          target_continents: @targeting.instance_variable_get('@target_continents').to_json,
          target_countries: @targeting.instance_variable_get('@target_countries').to_json,
          target_ips: @targeting.instance_variable_get('@target_ips').to_json,
          target_browsers: @targeting.instance_variable_get('@target_browsers').to_json
      }

      # Broadcast the notification
      @api_client.post('/broadcast', data_to_send)

      # Reset the targeting options
      reset_targeting

    end

    # Resets the targeting options
    def reset_targeting
      @targeting = Targeting.new
    end

    # Set which methods are private
    private :reset_targeting

  end

end