# libs
require 'rest_client'
require 'json'

# OpenStruct (Extended)
require 'ostruct'
require 'maestrano/open_struct'

# Version
require 'maestrano/version'

# XMLSecurity
require 'maestrano/xml_security/signed_document'

# SAML
require 'maestrano/saml/request'
require 'maestrano/saml/attribute_value'
require 'maestrano/saml/response'
require 'maestrano/saml/settings'
require 'maestrano/saml/validation_error'
require 'maestrano/saml/metadata'

# SSO
require 'maestrano/sso'
require 'maestrano/sso/base_user'
require 'maestrano/sso/base_group'
require 'maestrano/sso/base_membership'
require 'maestrano/sso/session'
require 'maestrano/sso/user'
require 'maestrano/sso/group'

# API Errors
require 'maestrano/api/error/base_error'
require 'maestrano/api/error/authentication_error'
require 'maestrano/api/error/connection_error'
require 'maestrano/api/error/invalid_request_error'

# API Operations
require 'maestrano/api/operation/base'
require 'maestrano/api/operation/create'
require 'maestrano/api/operation/delete'
require 'maestrano/api/operation/list'
require 'maestrano/api/operation/update'

# API
require 'maestrano/api/util'
require 'maestrano/api/object'
require 'maestrano/api/list_object'
require 'maestrano/api/resource'

# API - Account Entities
require 'maestrano/account/bill'
require 'maestrano/account/recurring_bill'

module Maestrano
  
  class << self
    attr_accessor :config
  end
  
  # Maestrano Configuration block
  def self.configure
    self.config ||= Configuration.new
    yield(config)
    self.config.post_initialize
    return self
  end
  
  # Check that app_id and api_key passed
  # in argument match
  def self.authenticate(app_id,api_key)
    self.param(:app_id) == app_id && self.param(:api_key) == api_key
  end
  
  # Take a user uid (either real or virtual)
  # and a group id and return the user uid that should
  # be used within the app based on the user_creation_mode
  # parameter:
  # 'real': then the real user uid is returned (usr-4d5sfd)
  # 'virtual': then the virtual user uid is returned (usr-4d5sfd.cld-g4f5d)
  def self.mask_user(user_uid,group_uid)
    sanitized_user_uid = self.unmask_user(user_uid)
    if Maestrano.param('sso.creation_mode') == 'virtual'
      return "#{sanitized_user_uid}.#{group_uid}"
    else
      return sanitized_user_uid
    end
  end
  
  # Take a user uid (either real or virtual)
  # and return the real uid part
  def self.unmask_user(user_uid)
    user_uid.split(".").first
  end
  
  # Get configuration parameter value
  # E.g:
  # Maestrano.param('api.key')
  # Maestrano.param(:api_key)
  def self.param(parameter)
    self.config.param(parameter)
  end
  
  # Return a hash describing the current
  # Maestrano configuration. The metadata
  # will be remotely fetched by Maestrano
  # Exclude any info containing an api key
  def self.to_metadata
    hash = {}
    hash['environment'] = self.param('environment')
    
    config_groups = ['app','api','sso','webhook']
    blacklist = ['api.key','api.token']
    
    config_groups.each do |cgroup_name|
      cgroup = self.config.send(cgroup_name)
      
      attr_list = cgroup.attributes.map(&:to_s)
      attr_list += Configuration::EVT_CONFIG[hash['environment']].keys.select { |k| k =~ Regexp.new("^#{cgroup_name}\.") }.map { |k| k.gsub(Regexp.new("^#{cgroup_name}\."),'') }
      attr_list.uniq!
      
      attr_list.each do |first_lvl|
        if cgroup.send(first_lvl).is_a?(OpenStruct)
          c2group = cgroup.send(first_lvl)
          c2group.attributes.each do |secnd_lvl|
            full_param = [cgroup_name,first_lvl,secnd_lvl].join('.')
            unless blacklist.include?(full_param)
              hash[cgroup_name.to_s] ||= {}
              hash[cgroup_name.to_s][first_lvl.to_s] ||= {}
              hash[cgroup_name.to_s][first_lvl.to_s][secnd_lvl.to_s] = self.param(full_param)
            end
          end
        else
          full_param = [cgroup_name,first_lvl].join('.')
          unless blacklist.include?(full_param)
            hash[cgroup_name.to_s] ||= {}
            hash[cgroup_name.to_s][first_lvl.to_s] = self.param(full_param)
          end
        end
      end
    end
    
    return hash
  end

  class Configuration
    attr_accessor :environment, :app, :sso, :api, :webhook

    def initialize
      @environment = 'test'
      
      # App config
      @app = OpenStruct.new({
        host: 'http://localhost:3000'
      })
      
      # API Config
      @api = OpenStruct.new({
        id: nil,
        key: nil,
        token: nil,
        version: nil,
        verify_ssl_certs: false,
        lang: nil, #set in post_initialize
        lang_version: nil #set in post_initialize
      })
      
      # SSO Config
      @sso = OpenStruct.new({
        enabled: true,
        slo_enabled: true,
        creation_mode: 'virtual',
        init_path: '/maestrano/auth/saml/init',
        consume_path: '/maestrano/auth/saml/consume',
        idm: @app.host
      })
      
      # WebHooks Config
      @webhook = OpenStruct.new({
        account: OpenStruct.new({
          groups_path: '/maestrano/account/groups/:id',
          group_users_path: '/maestrano/account/groups/:group_id/users/:id',
        })
      })
    end
    
    # Force or default certain parameters
    # Used after configure block
    def post_initialize
      self.api.token = "#{self.api.id}:#{self.api.key}"
      self.api.version = Maestrano::VERSION
      self.api.lang = 'ruby'
      self.api.lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
      self.sso.idm ||= self.app.host
      self.sso.slo_enabled &&= self.sso.enabled
    end
    
    # Transform legacy parameters into new parameter
    # style
    # Dummy mapping
    def legacy_param_to_new(parameter)
      case parameter.to_s
      when 'user_creation_mode'
        return 'sso.creation_mode'
      when 'verify_ssl_certs'
        return 'api.verify_ssl_certs'
      when 'app_id'
        return 'api.id'
      when /^app_(.*)/i
        return "app.#{$1}"
      when /^api_(.*)/i
        return "api.#{$1}"
      when /^sso_app_(.*)/i
        return "sso.#{$1}"
      when /^sso_(.*)/i
        return "sso.#{$1}"
      else
        return parameter.to_s
      end
    end
    
    # Handle legacy parameter assignment
    def method_missing(meth, *args, &block)
      if meth.to_s =~ /^((?:sso|app|api|user)_.*)=$/
        new_meth = self.legacy_param_to_new($1) + '='
        props = new_meth.split('.')
        last_prop = props.pop
        obj = props.inject(self,:send)
        obj.send(last_prop, *args, &block)
      else
        super
      end
    end
    
    # Get configuration parameter value
    def param(parameter)
      real_param = self.legacy_param_to_new(parameter)
      props = real_param.split('.')
      
      # Either respond to param directly or via properties chaining (e.g: webhook.account.groups_path)
      if self.respond_to?(real_param) || props.inject(self) { |result,elem| result && result.respond_to?(elem) ? result.send(elem) || elem : false }
        last_prop = props.pop
        obj = props.inject(self,:send)
        obj.send(last_prop)
      elsif EVT_CONFIG[@environment.to_s].has_key?(real_param.to_s)
        EVT_CONFIG[@environment.to_s][real_param.to_s]
      else
        raise ArgumentError, "No such configuration parameter: '#{parameter}'"
      end
    end
    
    EVT_CONFIG = {
      'test' => {
        'api.host'             => 'http://api-sandbox.maestrano.io',
        'api.base'             => '/api/v1/',
        'sso.idp'              => 'https://maestrano.com',
        'sso.name_id_format'   => Maestrano::Saml::Settings::NAMEID_PERSISTENT,
        'sso.x509_fingerprint' => '01:06:15:89:25:7d:78:12:28:a6:69:c7:de:63:ed:74:21:f9:f5:36',
        'sso.x509_certificate' => "-----BEGIN CERTIFICATE-----\nMIIDezCCAuSgAwIBAgIJAOehBr+YIrhjMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\nVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoT\nEU1hZXN0cmFubyBQdHkgTHRkMRYwFAYDVQQDEw1tYWVzdHJhbm8uY29tMSQwIgYJ\nKoZIhvcNAQkBFhVzdXBwb3J0QG1hZXN0cmFuby5jb20wHhcNMTQwMTA0MDUyMjM5\nWhcNMzMxMjMwMDUyMjM5WjCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEP\nMA0GA1UEBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQG\nA1UEAxMNbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVz\ndHJhbm8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVkIqo5t5Paflu\nP2zbSbzxn29n6HxKnTcsubycLBEs0jkTkdG7seF1LPqnXl8jFM9NGPiBFkiaR15I\n5w482IW6mC7s8T2CbZEL3qqQEAzztEPnxQg0twswyIZWNyuHYzf9fw0AnohBhGu2\n28EZWaezzT2F333FOVGSsTn1+u6tFwIDAQABo4HuMIHrMB0GA1UdDgQWBBSvrNxo\neHDm9nhKnkdpe0lZjYD1GzCBuwYDVR0jBIGzMIGwgBSvrNxoeHDm9nhKnkdpe0lZ\njYD1G6GBjKSBiTCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE\nBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQGA1UEAxMN\nbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVzdHJhbm8u\nY29tggkA56EGv5giuGMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCc\nMPgV0CpumKRMulOeZwdpnyLQI/NTr3VVHhDDxxCzcB0zlZ2xyDACGnIG2cQJJxfc\n2GcsFnb0BMw48K6TEhAaV92Q7bt1/TYRvprvhxUNMX2N8PHaYELFG2nWfQ4vqxES\nRkjkjqy+H7vir/MOF3rlFjiv5twAbDKYHXDT7v1YCg==\n-----END CERTIFICATE-----"
      },
      'production' => {
        'api.host'             => 'https://maestrano.com',
        'api.base'             => '/api/v1/',
        'sso.idp'              => 'https://maestrano.com',
        'sso.name_id_format'   => Maestrano::Saml::Settings::NAMEID_PERSISTENT,
        'sso.x509_fingerprint' => '2f:57:71:e4:40:19:57:37:a6:2c:f0:c5:82:52:2f:2e:41:b7:9d:7e',
        'sso.x509_certificate' => "-----BEGIN CERTIFICATE-----\nMIIDezCCAuSgAwIBAgIJAPFpcH2rW0pyMA0GCSqGSIb3DQEBBQUAMIGGMQswCQYD\nVQQGEwJBVTEMMAoGA1UECBMDTlNXMQ8wDQYDVQQHEwZTeWRuZXkxGjAYBgNVBAoT\nEU1hZXN0cmFubyBQdHkgTHRkMRYwFAYDVQQDEw1tYWVzdHJhbm8uY29tMSQwIgYJ\nKoZIhvcNAQkBFhVzdXBwb3J0QG1hZXN0cmFuby5jb20wHhcNMTQwMTA0MDUyNDEw\nWhcNMzMxMjMwMDUyNDEwWjCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEP\nMA0GA1UEBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQG\nA1UEAxMNbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVz\ndHJhbm8uY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD3feNNn2xfEz5/\nQvkBIu2keh9NNhobpre8U4r1qC7h7OeInTldmxGL4cLHw4ZAqKbJVrlFWqNevM5V\nZBkDe4mjuVkK6rYK1ZK7eVk59BicRksVKRmdhXbANk/C5sESUsQv1wLZyrF5Iq8m\na9Oy4oYrIsEF2uHzCouTKM5n+O4DkwIDAQABo4HuMIHrMB0GA1UdDgQWBBSd/X0L\n/Pq+ZkHvItMtLnxMCAMdhjCBuwYDVR0jBIGzMIGwgBSd/X0L/Pq+ZkHvItMtLnxM\nCAMdhqGBjKSBiTCBhjELMAkGA1UEBhMCQVUxDDAKBgNVBAgTA05TVzEPMA0GA1UE\nBxMGU3lkbmV5MRowGAYDVQQKExFNYWVzdHJhbm8gUHR5IEx0ZDEWMBQGA1UEAxMN\nbWFlc3RyYW5vLmNvbTEkMCIGCSqGSIb3DQEJARYVc3VwcG9ydEBtYWVzdHJhbm8u\nY29tggkA8WlwfatbSnIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQDE\nhe/18oRh8EqIhOl0bPk6BG49AkjhZZezrRJkCFp4dZxaBjwZTddwo8O5KHwkFGdy\nyLiPV326dtvXoKa9RFJvoJiSTQLEn5mO1NzWYnBMLtrDWojOe6Ltvn3x0HVo/iHh\nJShjAn6ZYX43Tjl1YXDd1H9O+7/VgEWAQQ32v8p5lA==\n-----END CERTIFICATE-----"
      }
    }
  end
end