# encoding: UTF-8
#
# Copyright (c) 2010-2017 GoodData Corporation. All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree.

require_relative '../rest/resource'

require_relative '../mixins/data_property_reader'
require_relative '../mixins/links'
require_relative '../mixins/rest_resource'
require_relative '../mixins/uri_getter'

module GoodData
  class Client < Rest::Resource
    data_property_reader 'id'

    attr_accessor :domain

    include Mixin::Links
    include Mixin::UriGetter

    CLIENT_TEMPLATE = {
      client: {
        id: nil,
        segment: nil,
        project: nil
      }
    }

    class << self
      def [](id, opts = {})
        domain = opts[:domain]
        segment = opts[:segment]
        fail ArgumentError, 'No :domain specified' if domain.nil?
        client = domain.client
        fail ArgumentError, 'No client specified' if client.nil?
        data_product = opts[:data_product] || (segment ? segment.data_product : nil)

        if id == :all
          tenants_uri = base_uri(domain, data_product)
          tenants_uri += "?segment=#{CGI.escape(segment.segment_id)}" if segment

          all_clients = []
          loop do
            res = client.get tenants_uri
            res['clients']['paging']['next']
            res['clients']['items'].each do |i|
              p = i['client']['project']
              tenant = client.create(GoodData::Client, i.merge('domain' => domain))
              tenant.project = p
              all_clients << tenant
            end
            url = res['clients']['paging']['next']
            break unless url
          end

          all_clients
        else
          id = id.respond_to?(:client_id) ? id.client_id : id
          tenant_uri = base_uri(domain, data_product)
          data = client.get(tenant_uri + "/#{CGI.escape(id)}")
          client.create(GoodData::Client, data.merge('domain' => domain))
        end
      end

      # Creates new client from parameters passed.
      #
      # Should not be called directly. Use GoodData::Segment#create_client.
      #
      # @param data [Hash] Data required to create the client
      # @option data id [String]
      # @option data project [GoodData::Project]
      # @param options [Hash] Options
      # @option options segment [GoodData::Segment]
      # @option options client [GoodData::Rest::Client]
      # @return [GoodData::Client] New GoodData::Client instance
      def create(data, options)
        segment = options[:segment]
        domain = segment.domain
        tenant = client.create(GoodData::Client, GoodData::Helpers.stringify_keys(CLIENT_TEMPLATE.merge(domain: domain)), domain: domain)
        tenant.tap do |s|
          s.project = data[:project]
          s.client_id = data[:id]
          s.segment = segment.uri
        end
      end
      alias_method :associate, :create

      def update_setting(name, value, opts = {})
        return nil unless value
        domain = opts[:domain]
        client_id = opts[:client_id]
        data_product_id = opts[:data_product_id]
        uri = data_product_id ? GoodData::DataProduct::ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product_id } : domain.segments_uri
        body = {
          setting: {
            name: "#{name}",
            value: "#{value}"
          }
        }
        domain.client.put(uri + "/clients/#{client_id}/settings/#{name}", body)
        nil
      end
      alias_method :add_setting, :update_setting

      def base_uri(domain, data_product)
        if data_product
          uri = GoodData::DataProduct::ONE_DATA_PRODUCT_PATH % { domain_name: domain.name, id: data_product.data_product_id }
        else
          uri = domain.segments_uri
        end
        uri + '/clients'
      end
    end

    def initialize(data)
      super(data)
      @domain = data.delete('domain')
      @json = data
    end

    # Segment id getter for the Segment. Called segment_id since id is a reserved word in ruby world
    #
    # @return [String] Segment id
    def client_id
      data['id']
    end

    def client_id=(a_name)
      data['id'] = a_name
      self
    end

    # Setter for the project this client has set
    #
    # @param a_project [String|GoodData::Project] Id or an instance of a project
    # @return [GoodData::Cliet] Returns the instance of the client
    def project=(a_project)
      data['project'] = a_project.respond_to?(:uri) ? a_project.uri : a_project
      self
    end

    # Project URI this client has set
    #
    # @return [String] Returns the URI of the project this client has set
    def project_uri
      data['project']
    end

    # Project this client has set
    #
    # @return [GoodData::Project] Returns the instance of the client's project
    def project
      client.projects(project_uri) if project?
    end

    # Returns boolean if client has a project provisioned
    #
    # @return [Boolean] Returns true if client has a project provisioned. False otherwise
    def project?
      project_uri != nil
    end

    # Reloads the client from the URI
    #
    # @return [GoodData::Client] Returns the updated client object
    def reload!
      res = client.get(uri)
      @json = res
      self
    end

    # Segment id setter which this client is connected to.
    #
    # @param a_segment [String] Id of the segment.
    # @return [GoodData::Client] Returns the instance of the client
    def segment=(a_segment)
      data['segment'] = a_segment.respond_to?(:uri) ? a_segment.uri : a_segment
      self
    end

    # Segment this client is connected to.
    #
    # @return [GoodData::Segment] Segment
    def segment
      segment_res = client.get(data['segment'])
      client.create(GoodData::Segment, segment_res)
    end

    # Segment URI this client is connected to.
    #
    # @return [String] Segment URI
    def segment_uri
      data['segment']
    end

    # Creates or updates a client instance on the API.
    #
    # @return [GoodData::Client] Client instance
    def save
      if uri
        client.put(uri, json)
      else
        data_product = segment.data_product
        res = client.post(self.class.base_uri(domain, data_product), json)
        @json = res
      end
      self
    end

    # Deletes a client instance on the API.
    #
    # @return [GoodData::Client] Segment instance
    def delete
      project.delete if project && !project.deleted?
      dissociate
    end

    # Deletes a client but maintain their project
    def dissociate
      client.delete(uri) if uri
    end

    def settings
      data_product = segment.data_product
      res = client.get(self.class.base_uri(domain, data_product) + "/#{client_id}/settings")
      settings = GoodData::Helpers.get_path(res, %w(settingsList items))
      settings.map do |setting|
        setting = setting['setting']
        {
          name: setting['name'],
          value: setting['value']
        }
      end
    end
  end
end
