# frozen_string_literal: true

module Capybara
  module Email
    class Driver < Capybara::Driver::Base
      attr_reader :email

      def initialize(email)
        @email = email
        super()
      end

      def follow(url)
        url = URI.parse(url)
        host = "#{url.scheme}://#{url.host}"
        host += ":#{url.port}" unless url.port == url.default_port
        host_with_path = File.join(host, url.path)

        Capybara
          .current_session
          .visit([host_with_path, url.query].compact.join('?'))
      end

      def body
        dom.to_xml
      end

      # Nokogiri object for traversing content
      #
      # @return Nokogiri::HTML::Document
      def dom
        @dom ||= Nokogiri::HTML(source)
      end

      # Find elements based on given xpath
      #
      # @param [xpath string]
      #
      # @return [Array<Capybara::Driver::Node>]
      def find(selector)
        dom.xpath(selector).map { |node| Capybara::Email::Node.new(self, node) }
      end

      alias find_xpath find

      def find_css(selector)
        dom.css(selector).map { |node| Capybara::Email::Node.new(self, node) }
      end

      # String version of email HTML source
      #
      # @return String
      def source
        if email.mime_type == 'text/plain'
          convert_to_html(raw)
        else
          raw
        end
      end

      # Plain text email contents
      #
      # @return String
      def raw
        if email.mime_type =~ %r{\Amultipart/(alternative|related|mixed)\Z}
          if email.html_part
            return email.html_part.body.to_s
          elsif email.text_part
            return email.text_part.body.to_s
          end
        end

        email.body.to_s
      end

      private

      def method_missing(method_name, *args)
        if email.respond_to?(method_name)
          if args.empty?
            email.send(method_name)
          else
            email.send(method_name, args)
          end
        else
          super
        end
      end

      def respond_to_missing?(method_name, include_private = false)
        email.respond_to?(method_name, include_private || super)
      end

      def convert_to_html(text)
        "<html><body>#{convert_links(text)}</body></html>"
      end

      def convert_links(text)
        text.gsub(%r{(https?://\S+)}, %q(<a href="\1">\1</a>))
      end
    end
  end
end
