require 'date'
require 'openssl'

module SSLCheck
  class Certificate
    def initialize(cert, clock=nil)
      @cert = bootstrap_certificate(cert)
      @clock = clock || DateTime
    end

    def to_x509
      OpenSSL::X509::Certificate.new @cert.to_s
    end

    def to_h
      {
        :common_name       => common_name,
        :organization_unit => organizational_unit,
        :not_before        => not_before,
        :not_after         => not_after,
        :issued            => true,
        :expired           => false,
        :issuer            => {
          :common_name  => issuer_common_name,
          :country      => issuer_country,
          :state        => issuer_state,
          :locality     => issuer_locality,
          :organization => issuer_organization
        }
      }
    end

    def to_s
      @cert.to_s
    end

    def subject
      @cert.subject.to_s
    end

    def organizational_unit
      match = subject.match(/OU=([\w\s]+)/)
      match.captures.first if match
    end

    def common_name
      subject.scan(/CN=(.*)/)[0][0]
    end

    def alternate_common_names
      ext = @cert.extensions.find{|ext| ext.oid == "subjectAltName" }
      return [] unless ext
      alternates = ext.value.split(",")
      names = alternates.map{|a| a.scan(/DNS:(.*)/)[0][0]}
      names
    end

    def issuer
      @cert.issuer.to_s
    end

    def issuer_country
      match = issuer.match(/C=([\w\s]+)/)
      match.captures.first if match
    end

    def issuer_state
      match = issuer.match(/ST=([\w\s]+)/)
      match.captures.first if match
    end

    def issuer_locality
      match = issuer.match(/L=([\w\s]+)/)
      match.captures.first if match
    end

    def issuer_organization
      match = issuer.match(/O=([^\/]+)/)
      match.captures.first if match
    end

    def issuer_common_name
      issued_by
    end

    def issued_by
      match = issuer.match("CN=(.*)")
      match.captures.first if match
    end

    def public_key
      @cert.public_key
    end

    def verify(ca)
      @cert.verify(ca.public_key)
    end

    def not_before
      DateTime.parse(@cert.not_before.to_s)
    end

    def not_after
      DateTime.parse(@cert.not_after.to_s)
    end

    def expired?
      @clock.now > not_after
    end

    def expires_in?(num_days)
      (@clock.now.beginning_of_day + num_days.days) >= not_after.beginning_of_day
    end

    def issued?
      @clock.now > not_before
    end

    def bootstrap_certificate(cert)
      return cert if cert.is_a?(OpenSSL::X509::Certificate)
      return cert if cert.is_a?(SSLCheck::Certificate)
      OpenSSL::X509::Certificate.new cert
    end

  end
end
