module Bebox
  class NodeWizard
    include Bebox::Logger
    include Bebox::WizardsHelper
    include Bebox::VagrantHelper

    # Create a new node
    def create_new_node(project_root, environment)
      # Ask the hostname for node
      hostname = ask_not_existing_hostname(project_root, environment)
      # Ask IP for node
      ip = ask_ip(environment)
      # Node creation
      node = Bebox::Node.new(environment, project_root, hostname, ip)
      output = node.create
      ok 'Node created!.'
      return output
    end

    # Removes an existing node
    def remove_node(project_root, environment, hostname)
      # Ask for a node to remove
      nodes = Bebox::Node.list(project_root, environment, 'nodes')
      if nodes.count > 0
        hostname = choose_option(nodes, 'Choose the node to remove:')
      else
        error "There are no nodes in the '#{environment}' environment to remove. No changes were made."
        return true
      end
      # Ask for deletion confirmation
      return warn('No changes were made.') unless confirm_action?('Are you sure that you want to delete the node?')
      # Node deletion
      node = Bebox::Node.new(environment, project_root, hostname, nil)
      output = node.remove
      ok 'Node removed!.'
      return output
    end

    # Associate a role with a node in a environment
    def set_role(project_root, environment)
      roles = Bebox::Role.list(project_root)
      nodes = Bebox::Node.list(project_root, environment, 'nodes')
      node = choose_option(nodes, 'Choose an existing node:')
      role = choose_option(roles, 'Choose an existing role:')
      output = Bebox::Provision.associate_node_role(project_root, environment, node, role)
      ok 'Role associated to node!.'
      return output
    end

    # Prepare the nodes in a environment
    def prepare(project_root, environment)
      # Check already prepared nodes
      nodes_to_prepare = check_nodes_to_prepare(project_root, environment)
      # Output the nodes to be prepared
      if nodes_to_prepare.count > 0
        title 'Preparing nodes:'
        nodes_to_prepare.each{|node| msg(node.hostname)}
        linebreak
        # For all environments regenerate the deploy file
        Bebox::Node.regenerate_deploy_file(project_root, environment, nodes_to_prepare)
        # If environment is 'vagrant' Prepare and Up the machines
        if environment == 'vagrant'
          Bebox::VagrantHelper.generate_vagrantfile(nodes_to_prepare)
          nodes_to_prepare.each{|node| prepare_vagrant(node)}
          Bebox::VagrantHelper.up_vagrant_nodes(project_root)
        end
        # For all the environments do the preparation
        nodes_to_prepare.each do |node|
          node.prepare
          ok 'Node prepared!.'
        end
      else
        warn 'There are no nodes to prepare. No changes were made.'
      end
      return true
    end

    # Check the nodes already prepared and ask confirmation to re-do-it
    def check_nodes_to_prepare(project_root, environment)
      nodes_to_prepare = []
      nodes = Bebox::Node.nodes_in_environment(project_root, environment, 'nodes')
      prepared_nodes = Bebox::Node.list(project_root, environment, 'prepared_nodes')
      nodes.each do |node|
        if prepared_nodes.include?(node.hostname)
          checkpoint_status = "(start: #{node.checkpoint_parameter_from_file('prepared_nodes', 'started_at')} - end: #{node.checkpoint_parameter_from_file('prepared_nodes', 'finished_at')})"
          message = "The node '#{node.hostname}' was already prepared #{checkpoint_status}.\nDo you want to re-prepare it?"
          nodes_to_prepare << node if confirm_action?(message)
        else
          nodes_to_prepare << node
        end
      end
      nodes_to_prepare
    end

    # Check if there's an existing node in a environment
    def node_exists?(project_root, environment, node_name)
      File.exists?("#{project_root}/.checkpoints/environments/#{environment}/nodes/#{node_name}.yml")
    end

    # Keep asking for a hostname that not exist
    def ask_not_existing_hostname(project_root, environment)
      hostname = ask_hostname(project_root, environment)
      # Check if the node not exist
      if node_exists?(project_root, environment, hostname)
        error 'A hostname with that name already exist. Try a new one.'
        ask_hostname(project_root, environment)
      else
        return hostname
      end
    end

    # Ask for the hostname
    def ask_hostname(project_root, environment)
      write_input('Write the hostname for the node:', nil, /\.(.*)/, 'Enter valid hostname. Ex. host.server1.com')
    end

    # Ask for the ip until is valid
    def ask_ip(environment)
      ip = write_input('Write the IP address for the node:', nil, /\.(.*)/, 'Enter a valid IP address. Ex. 192.168.0.50')
      # If the environment is not vagrant don't check ip free
      return ip if environment != 'vagrant'
      # Check if the ip address is free
      if free_ip?(ip)
        return ip
      else
        error 'The IP address is not free. Try a new one.'
        ask_ip(environment)
      end
    end

    # Validate if the IP address is free
    def free_ip?(ip)
      `ping -q -c 1 -W 3000 #{ip}`
      ($?.exitstatus == 0) ? false : true
    end
  end
end