#
# Copyright (C) 2013 Conjur Inc
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#

module Conjur
  module WebServer
    # Launch a web server which serves local files and proxies to the remote Conjur API.
    class Server
      def start(root)
        require 'rack'
        require 'conjur/webserver/login'
        require 'conjur/webserver/authorize'
        require 'conjur/webserver/api_proxy'
        require 'conjur/webserver/home'
        require 'conjur/webserver/conjur_info'

        sessionid = self.sessionid
        cookie_options = {
          secret: SecureRandom.hex(32),
          expire_after: 24*60*60
        }

        api_stack = [
          [Rack::Session::Cookie, cookie_options],
          #[Conjur::WebServer::Authorize, sessionid],
          [Conjur::WebServer::ConjurInfo]
        ]

        app = Rack::Builder.app do
          map "/login" do
            use Rack::Session::Cookie, cookie_options
            run Conjur::WebServer::Login.new sessionid
          end
          map "/api" do
            api_stack.each{|args| use *args}
            run Conjur::WebServer::APIProxy.new
          end
          %w(js css fonts images).each do |path|
            map "/#{path}" do
              run Rack::File.new(File.join(root, path), 'Cache-Control' => 'max-age=0')
            end
          end
          map "/ui" do
            run Conjur::WebServer::Home.new(root)
          end
        end
        options = {
          app:  app,
          Port: port,
          Threads: '0:64',
          Verbose: true
        }

        # this vivifies the env for correct url settings
        # otherwise puma sets it to development and
        # confusion ensues
        # HR: it won't work anymore since API 4.10.2 (because it relies on sticky configuration feature which is removed)
        #     instead we just explicitly set RACK_ENV to "production" on the command call  -- this is also bad, but... good enough for now, until we'll fix API configuration logic
        Conjur.configuration.env

        Rack::Server.start(options)
      end
      
      def open
        require 'launchy'
        url = "http://localhost:#{port}/login?sessionid=#{sessionid}"
        # as launchy sometimes silently fails, we need human-friendly failover
        $stderr.puts "If your browser did not opened the UI automatically, point it to #{url}" 
        Launchy.open(url)
      end
      
      protected
      
      def port
        @port ||= find_available_port
      end

      DEFAULT_PORT = 42_289
      
      def find_available_port
        begin
          server = TCPServer.new('127.0.0.1', 0)
          server.addr[1]
        ensure
          server.close if server
        end
      end
      
      def sessionid
        require 'securerandom'
        @sessionid ||= SecureRandom.hex(32)
      end
    end
  end
end