require 'open3'
require 'colorize'

module Headhunter
  class HtmlValidator
    VALIDATOR_DIR = Gem.loaded_specs['headhunter'].full_gem_path + '/lib/tidy/'
    EXECUTABLE    = 'tidy'

    attr_reader :responses

    def initialize
      @responses = []
    end

    def validate(uri, html)
      Dir.chdir(VALIDATOR_DIR) do
        raise "Could not find tidy in #{Dir.pwd}" unless File.exists? EXECUTABLE

        # Docs for Tidy: http://tidy.sourceforge.net/docs/quickref.html
        stdin, stdout, stderr = Open3.popen3("#{EXECUTABLE} -quiet")
        stdin.puts html
        stdin.close
        stdout.close

        @responses << Response.new(stderr.read, uri)
        stderr.close
      end
    end

    def valid_responses
      @responses.select(&:valid?)
    end

    def invalid_responses
      @responses.reject(&:valid?)
    end

    def statistics
      lines = []

      lines << "Validated #{responses.size} pages.".yellow
      lines << "All pages are valid.".green unless invalid_responses.any?
      lines << "#{x_pages_be(invalid_responses.size)} invalid.".red if invalid_responses.any?

      invalid_responses.each do |response|
        lines << "  #{response.uri}:".red

        response.errors.each do |error|
          lines << "    - #{error.to_s}".red
        end
      end

      lines.join("\n")
    end

    def x_pages_be(size)
      if size <= 1
        "#{size} page is"
      else
        "#{size} pages are"
      end
    end

    class Response
      attr_reader :uri

      def initialize(response = nil, uri = nil)
        @response = response
        @uri      = uri
      end

      def valid?
        @response.empty?
      end

      def errors
        @response.split("\n").map do |error|
          matches = error.match(/line (\d*) column (\d*) - (.*)/)
          Error.new( matches[1],
                     matches[3],
                     column: matches[2]
                   )
        end
      end

      class Error
        attr_reader :line, :message, :details

        def initialize(line, message, details = {})
          @line    = line
          @message = message
          @details = details
        end

        def to_s
          "Line #{@line}, column #{@details[:column]}: #{@message}."
        end
      end
    end
  end
end
