require "xcpretty/ansi"

module XCPretty

  module Printer

    module Matchers
      # @regex Captured groups
      # $1 = suite
      # $2 = time
      TESTS_RUN_START_MATCHER = /Test Suite '(?:.*\/)?(.*[ox]ctest.*)' started at(.*)/

      # @regex Captured groups
      # $1 = suite
      # $2 = time
      TESTS_RUN_COMPLETION_MATCHER = /Test Suite '(?:.*\/)?(.*[ox]ctest.*)' finished at(.*)/

      # @regex Captured groups
      # $1 = class
      # $2 = test_case
      # $3 = time
      PASSING_TEST_MATCHER = /Test Case\s'-\[(.*)\s(.*)\]'\spassed\s\((\d*\.\d{3})\sseconds\)/

      # @regex Captured groups
      # $1 = file
      # $2 = test_suite
      # $3 = test_case
      # $4 = reason
      FAILING_TEST_MATCHER = /(.+:\d+):\serror:\s[\+\-]\[(.*)\s(.*)\]\s:(?:\s'.*'\s\[FAILED\],)?\s(.*)/

      # @regex Captured groups
      # $1 test suite name
      TEST_SUITE_START_MATCHER = /Test Suite '(.*)' started at/
      EXECUTED_MATCHER = /^Executed/
    end

    include ANSI
    include Matchers

    def use_unicode=(bool)
      @use_unicode = !!bool
    end

    def use_unicode?
      !!@use_unicode
    end

    def pretty_print(text)
      update_test_state(text)
      formatted_text = pretty_format(text)
      formatted_text = format_test_summary(text) if formatted_text.empty?

      STDOUT.print(formatted_text + optional_newline) unless formatted_text.empty?
    end

    def update_test_state(text)
      case text
      when TESTS_RUN_START_MATCHER
        @tests_done = false
        @printed_summary = false
        @failures = {}
      when TESTS_RUN_COMPLETION_MATCHER
        @tests_done = true
      when FAILING_TEST_MATCHER
        store_failure($1, $2, $3, $4)
      end
    end

    def format_test_summary(text)
      if text =~ EXECUTED_MATCHER && @tests_done && !@printed_summary
        @printed_summary = true
        test_summary(text)
      else
        ""
      end
    end

    def optional_newline
      ""
    end

    def project_build_info(text)
      target = text.split('TARGET').last.split('OF PROJECT').first
      clean_target = target.split('-').last.strip
      project = text.split('OF PROJECT').last.split('WITH').first.strip
      configuration = text.split('CONFIGURATION').last.split('===').first.strip
      {
        :target => clean_target,
        :project => project,
        :configuration => configuration
      }
    end

    def test_summary(executed_message)
      formatted_suites = failures_per_suite.map do |suite, failures|
        formatted_failures = failures.map do |f|
          "  #{f[:test_case]}, #{f[:reason]}\n  #{f[:file]}"
        end.join("\n\n")

        "#{suite}\n#{formatted_failures}"
      end

      final_message = if colorize?
                        formatted_suites.any? ? red(executed_message) : green(executed_message)
                      else
                        executed_message
                      end
      text = [formatted_suites, final_message].join("\n\n\n").strip
      "\n\n#{text}"
    end

    def failures_per_suite
      @failures ||= {}
    end

    def store_failure(file, test_suite, test_case, reason)
      failures_per_suite[test_suite] ||= []
      failures_per_suite[test_suite] << {
        :file => cyan(file),
        :reason => red(reason),
        :test_case => test_case,
      }
    end
  end
end