require 'rubygems'
require 'rest_client'
require 'zip'
require 'zip/zipfilesystem'
require 'zip/zip'
require 'open-uri'
require 'digest/sha2'
require 'openssl'
require 'base64'
require 'json'


module Factor
  module Client
    class Client
      attr_accessor :host
      FACTOR_HOST = ENV["FACTOR_HOST"] || "factor.io"
    
      def initialize(host=FACTOR_HOST)
        @host=host
      end
    
      def login_token(token)
        @token=token
      end
    
      def create_credential(service,name,value,secret=nil,organization=nil)
        # this is a PUT not POST because it is technically editing, not creating a new one
        credential = {:service=>service,:name=>name,:value=>value}

        if secret
          payload=value
          sha256= Digest::SHA2.new(256)
          encrypter = OpenSSL::Cipher.new("AES-256-CFB")
          encrypter.encrypt
          encrypter.key=Base64.encode64(sha256.digest(secret))

          encrypted = Base64.encode64(encrypter.update(value) + encrypter.final)
          credential[:value]=encrypted
          credential[:encrypted]=true
        end
        path = !!organization ? "organizations/#{organization}/credentials" : "credentials"
        rest_post(path,credential)
      end
      
      def get_credentials()
        rest_get("credentials")
      end
      
      def delete_credential(service,name)
        rest_delete("credentials",{:service=>service,:name=>name})
      end
    
    
    
    
      # untested
      def create_workflow(key,definition,organization=nil)
        path = !!organization ? "organizations/#{organization}/workflows" : "workflows"
        rest_post(path,{:workflow=>{:name=>key,:definition=>definition}})
      end
      
      def get_workflows()
        rest_get("workflows")
      end
      
      def delete_workflow(workflow_name)
        rest_delete("workflows/#{workflow_name}")
      end

      def start_workflow(workflow_name)
        rest_get("workflows/#{workflow_name}/state")
      end

      def stop_workflow(workflow_name)
        rest_delete("workflows/#{workflow_name}/state")
      end

      def call_workflow(workflow_name,params)
        rest_get("workflows/#{workflow_name}/call",params)
      end

      
      
      # channels
      def create_channel(path,definition_file,organization=nil)
        file=zip(File.expand_path(path))
        definition = File.read(definition_file)

        path = !!organization ? "organizations/#{organization}/channels" : "channels"
        rest_post(path,{:zip=>file,:channel=>{:definition=>definition}})
      end
      
      def get_channels()
        rest_get("channels")
      end
      
      def delete_channel(channel_name)
        rest_delete("channels/#{channel_name}")
      end
      
      
    

    
    
      private
    
      def zip(directory)
        path=directory.dup
        path.sub!(%r[/$],'')
        zip_path = File.join(path,"..",File.basename(path))+'.zip'
        FileUtils.rm zip_path, :force=>true
        Zip::ZipFile.open(zip_path, 'w') do |zip|
          Dir["#{path}/**/**"].reject{|f|f==zip_path}.each do |file|
            zip.add(file.sub(path+'/',''),file)
          end
        end
        File.open(zip_path,'r')
      end

      def rest_get(path,params={})
        params["auth_token"]=@token
        
        param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
        response=RestClient.get("http://#{@host}/#{path}.json?#{param_string}")
        JSON.parse(response)
      end
      
      def rest_put(path,params={})
        params["auth_token"]=@token
        response=RestClient.put("http://#{@host}/#{path}.json",params)
        JSON.parse(response)
      end
      
      def rest_post(path,params={})
        response=RestClient.post("http://#{@host}/#{path}.json?auth_token=#{@token}",params)
        JSON.parse(response)
      end
      
      def rest_delete(path,params={})
        params["auth_token"]=@token
        
        param_string=params.map{|key,value| "#{key}=#{value}"}.join("&")
        response=RestClient.delete("http://#{@host}/#{path}.json?#{param_string}")
        JSON.parse(response)
      end
    
    end
  end
end