require "pact_broker/logging"
require "pact_broker/repositories"
require "pact_broker/matrix/deployment_status_summary"
require "pact_broker/matrix/query_results_with_deployment_status_summary"
require "pact_broker/messages"
require "pact_broker/string_refinements"

module PactBroker
  module Matrix
    module Service

      extend self
      extend PactBroker::Repositories
      extend PactBroker::Services
      include PactBroker::Logging
      extend PactBroker::Messages
      using PactBroker::StringRefinements

      def can_i_deploy(selectors, options = {})
        # No point doing the deployment status summary if no versions are specified.
        query_results = find(selectors, options)
        QueryResultsWithDeploymentStatusSummary.new(query_results, DeploymentStatusSummary.new(query_results))
      end

      def can_i_merge(pacticipant_name: nil, pacticipant: nil, latest_main_branch_version: nil)
        # first we find the pacticipant by name (or use the one passed in) if pacticipant is nil
        if pacticipant.nil?
          pacticipant = pacticipant_service.find_pacticipant_by_name(pacticipant_name)
          raise PactBroker::Error.new("No pacticipant found with name '#{pacticipant_name}'") unless pacticipant
        else
          pacticipant_name = pacticipant.name
        end
         
        # then we find the latest version from the main branch if not passed in
        if latest_main_branch_version.nil?
          latest_main_branch_version = version_service.find_latest_version_from_main_branch(pacticipant)
          raise PactBroker::Error.new("No main branch version found for pacticipant '#{pacticipant_name}'") unless latest_main_branch_version
        end

        selectors = PactBroker::Matrix::UnresolvedSelector.from_hash(
          pacticipant_name: pacticipant_name, 
          pacticipant_version_number: latest_main_branch_version.number
        )
         
        options = { main_branch: true, latest: true, latestby: "cvp" }
        query_results = can_i_deploy([selectors], options)
   
        query_results.deployable?
      end

      def find selectors, options = {}
        logger.info "Querying matrix", selectors: selectors, options: options
        matrix_repository.find(selectors, options)
      end

      def find_for_consumer_and_provider params, options = {}
        selectors = [ UnresolvedSelector.new(pacticipant_name: params[:consumer_name]), UnresolvedSelector.new(pacticipant_name: params[:provider_name]) ]
        can_i_deploy(selectors, options)
      end

      def find_for_consumer_and_provider_with_tags params
        consumer_selector = UnresolvedSelector.new(
          pacticipant_name: params[:consumer_name],
          tag: params[:tag],
          latest: true
        )
        provider_selector = UnresolvedSelector.new(
          pacticipant_name: params[:provider_name],
          tag: params[:provider_tag],
          latest: true
        )
        selectors = [consumer_selector, provider_selector]
        options = { latestby: "cvpv" }
        if validate_selectors(selectors).empty?
          matrix_repository.find(selectors, options).first
        else
          nil
        end
      end

      # TODO create a proper contract for this
      # rubocop: disable Metrics/CyclomaticComplexity
      def validate_selectors selectors, options = {}
        error_messages = []

        selectors.each do | s |
          if s[:pacticipant_name].nil?
            error_messages << "Please specify the pacticipant name"
          else
            # TODO a bunch more validation
            if s.key?(:pacticipant_version_number) && s.key?(:latest)
              error_messages << "A version number and latest flag cannot both be specified for #{s[:pacticipant_name]}"
            end
          end
        end

        selectors.collect{ |selector| selector[:pacticipant_name] }.compact.each do | pacticipant_name |
          unless pacticipant_service.find_pacticipant_by_name(pacticipant_name)
            error_messages << "Pacticipant #{pacticipant_name} not found"
          end
        end

        if selectors.size == 0
          error_messages << "Please provide 1 or more version selectors."
        end

        error_messages + validate_options(options)
      end
      # rubocop: enable Metrics/CyclomaticComplexity

      # rubocop: disable Metrics/CyclomaticComplexity
      def validate_options(options = {})
        error_messages = []

        options.fetch(:ignore_selectors, []).each do | s |
          if s[:pacticipant_name].nil?
            error_messages << "Please specify the pacticipant name to ignore"
          else
            if s.key?(:pacticipant_version_number) && s.key?(:latest)
              error_messages << "A version number and latest flag cannot both be specified for #{s[:pacticipant_name]} to ignore"
            end
          end
        end

        destination_identifiers = [options[:tag], options[:environment_name], options[:main_branch]&.to_s].compact

        if destination_identifiers.size > 1
          error_messages << message("errors.validation.cannot_specify_more_than_one_destination_identifier")
        end

        if options[:latest] && options[:environment_name]&.not_blank?
          error_messages << message("errors.validation.cannot_specify_latest_and_environment")
        end

        if options[:environment_name]&.not_blank? && environment_service.find_by_name(options[:environment_name]).nil?
          error_messages << message("errors.validation.environment_with_name_not_found", name: options[:environment_name])
        end

        if options[:limit] && options[:limit].to_i < 1
          error_messages << message("errors.validation.invalid_limit")
        end

        error_messages
      end
      # rubocop: enable Metrics/CyclomaticComplexity
    end
  end
end
