# frozen_string_literal: true

require 'cucumber/formatter/http_io'
require 'cucumber/formatter/url_reporter'
require 'cucumber/cli/options'

module Cucumber
  module Formatter
    module Io
      module_function

      def ensure_io(path_or_url_or_io, error_stream)
        return nil if path_or_url_or_io.nil?
        return path_or_url_or_io if io?(path_or_url_or_io)

        io = if url?(path_or_url_or_io)
               url = path_or_url_or_io
               reporter = url.start_with?(Cucumber::Cli::Options::CUCUMBER_PUBLISH_URL) ? URLReporter.new(error_stream) : NoReporter.new
               HTTPIO.open(url, nil, reporter)
             else
               File.open(path_or_url_or_io, Cucumber.file_mode('w'))
             end
        @io_objects_to_close ||= []
        @io_objects_to_close.push(io)
        io
      end

      module ClassMethods
        def new(*args, &block)
          instance = super

          config = args[0]
          if config.respond_to? :on_event
            config.on_event :test_run_finished do
              ios = instance.instance_variable_get(:@io_objects_to_close) || []
              ios.each do |io|
                at_exit do
                  unless io.closed?
                    io.flush
                    io.close
                  end
                end
              end
            end
          end

          instance
        end
      end

      def self.included(formatter_class)
        formatter_class.extend(ClassMethods)
      end

      def io?(path_or_url_or_io)
        path_or_url_or_io.respond_to?(:write)
      end

      def url?(path_or_url_or_io)
        path_or_url_or_io.match(/^https?:\/\//)
      end

      def ensure_file(path, name)
        raise "You *must* specify --out FILE for the #{name} formatter" unless path.instance_of? String
        raise "I can't write #{name} to a directory - it has to be a file" if File.directory?(path)
        raise "I can't write #{name} to a file in the non-existing directory #{File.dirname(path)}" unless File.directory?(File.dirname(path))

        ensure_io(path, nil)
      end

      def ensure_dir(path, name)
        raise "You *must* specify --out DIR for the #{name} formatter" unless path.instance_of? String
        raise "I can't write #{name} reports to a file - it has to be a directory" if File.file?(path)

        FileUtils.mkdir_p(path) unless File.directory?(path)
        File.absolute_path path
      end
    end
  end
end
