require 'security'
require 'highline/import' # to hide the entered password

module Deliver
  # Handles reading out the password from the keychain or asking for login data
  class PasswordManager
    # @return [String] The username / email address of the currently logged in user
    attr_accessor :username
    # @return [String] The password of the currently logged in user
    attr_accessor :password

    HOST = "deliver" # there might be a string appended, if user has multiple accounts
    private_constant :HOST

    # A singleton object, which also makes sure, to use the correct Apple ID
    # @param id_to_use (String) The Apple ID email address which should be used
    def self.shared_manager(id_to_use = nil)
      @@instance ||= PasswordManager.new(id_to_use)
    end

    # A new instance of PasswordManager.
    # 
    # This already check the Keychain if there is a username and password stored.
    # If that's not the case, it will ask for login data via stdin
    # @param id_to_use (String) Apple ID (e.g. steve@apple.com) which should be used for this upload.
    #  if given, only the password will be asked/loaded.
    def initialize(id_to_use = nil)
      self.username ||= ENV["DELIVER_USER"] || id_to_use || load_from_keychain[0]
      self.password ||= ENV["DELIVER_PASSWORD"] || load_from_keychain[1]

      if (self.username || '').length == 0 or (self.password || '').length == 0
        puts "No username or password given. You can set environment variables:"
        puts "DELIVER_USER, DELIVER_PASSWORD"

        ask_for_login
      end
    end

    # This method is called, when the iTunes backend returns that the login data is wrong
    # This will ask the user, if he wants to re-enter the password
    def password_seems_wrong
      return false if Helper.is_test?
      
      puts "It seems like the username or password for the account '#{self.username}' is wrong."
      reenter = agree("Do you want to re-enter your username and password? (y/n)", true)
      if reenter
        remove_from_keychain

        @username = nil
        @password = nil

        puts "You will have to re-run the recent command to use the new username/password."
        return true
      else
        return false
      end
    end

    private
      def ask_for_login
        puts "--------------------------------------------------------------------------".green
        puts "The login information you enter now will be stored in your keychain       ".green
        puts "More information about that on GitHub: https://github.com/krausefx/deliver".green
        puts "--------------------------------------------------------------------------".green

        username_was_there = self.username

        while (self.username || '').length == 0
          self.username = ask("Username: ")
        end

        self.password ||= load_from_keychain[1] # maybe there was already something stored in the keychain

        if (self.password || '').length > 0
          return true
        else
          while (self.password || '').length == 0
            text = "Password: "
            text = "Password (for #{self.username}): " if username_was_there
            self.password = ask(text) { |q| q.echo = "*" }
          end

          # Now we store this information in the keychain
          # Example usage taken from https://github.com/nomad/cupertino/blob/master/lib/cupertino/provisioning_portal/commands/login.rb
          if Security::InternetPassword.add(hostname, self.username, self.password)
            return true
          else
            Helper.log.error "Could not store password in keychain"
            return false
          end
        end
      end

      def remove_from_keychain
        puts "removing keychain item: #{hostname}"
        Security::InternetPassword.delete(:server => hostname)
      end
    
      def load_from_keychain
        pass = Security::InternetPassword.find(:server => hostname)
        
        return [pass.attributes['acct'], pass.password] if pass
        return [nil, nil]
      end

      def hostname
        [HOST, self.username].join('.')
      end
  end
end