require_relative 'yaml/stack_file_loader'

module Kontena::Cli::Stacks
  class ChangeResolver

    attr_reader :old_data, :new_data

    # Creates a change analysis from two sets of stack data.
    # The format is a flat hash of all related stacks.
    #
    # @param old_data [Hash]
    # @param new_data [Hash]
    def initialize(old_data, new_data)
      @old_data = old_data
      @new_data = new_data
      analyze
    end

    # @return [Array<String>] an array of services that should be added
    def added_services
      @added_services ||= []
    end

    # @return [Array<String>] an array of services that should be removed
    def removed_services
      @removed_services ||= []
    end

    # @return [Array<String>] an array of services that should be upgraded
    def upgraded_services
      @upgraded_services ||= []
    end

    # @return [Array<String>] an array of stack installation names that should be removed
    def removed_stacks
      @removed_stacks ||= []
    end

    # @return [Array<String>] an array of stack installation names that should be removed
    def added_stacks
      @added_stacks ||= []
    end

    # @return [Array<String>] an array of stack installation names that should be upgraded
    def upgraded_stacks
      @upgraded_stacks ||= []
    end

    # @return [Hash] a hash of "installed-stack-name" => { :from => 'stackname', :to => 'new-stackname' }
    def replaced_stacks
      @replaced_stacks ||= {}
    end

    # @return [Array<String>] an array of installed stack names that should exist after upgrade
    def remaining_stacks
      @remaining_stacks ||= added_stacks + upgraded_stacks
    end

    def analyze
      old_names = old_data.keys
      new_names = new_data.keys

      removed_stacks.concat(old_names - new_names)
      added_stacks.concat(new_names - old_names)
      upgraded_stacks.concat(new_names & old_names)

      removed_stacks.each do |removed_stack|
        removed_services.concat(
          old_data[removed_stack][:stack_data]['services'].map { |svc| "#{removed_stack}/#{svc['name']}"}
        )
      end

      added_stacks.each do |added_stack|
        added_services.concat(
          new_data[added_stack][:stack_data]['services'].map { |svc| "#{added_stack}/#{svc['name']}"}
        )
      end

      upgraded_stacks.each do |upgraded_stack|
        old_stack = old_data[upgraded_stack][:stack_data]['stack']
        new_stack = new_data[upgraded_stack][:stack_data]['stack']

        unless old_stack == new_stack
          replaced_stacks[upgraded_stack] = { from: old_stack, to: new_stack }
        end

        old_services = old_data[upgraded_stack][:stack_data]['services'].map { |svc| "#{upgraded_stack}/#{svc['name']}" }
        new_services = new_data[upgraded_stack][:stack_data]['services'].map { |svc| "#{upgraded_stack}/#{svc['name']}" }

        removed_services.concat(old_services - new_services)
        added_services.concat(new_services - old_services)
        upgraded_services.concat(new_services & old_services)
      end
    end
  end
end