module CertificateAuthority
  module Extensions
    module ExtensionAPI
      def to_s
        raise "Implementation required"
      end

      def config_extensions
        {}
      end

      def openssl_identifier
        raise "Implementation required"
      end
    end

    class BasicContraints
      include ExtensionAPI
      include ActiveModel::Validations
      attr_accessor :ca
      attr_accessor :path_len
      validates :ca, :inclusion => [true,false]

      def initialize
        self.ca = false
      end

      def is_ca?
        self.ca
      end

      def path_len=(value)
        raise "path_len must be a non-negative integer" if value < 0 or !value.is_a?(Fixnum)
        @path_len = value
      end

      def openssl_identifier
        "basicConstraints"
      end

      def to_s
        result = ""
        result += "CA:#{self.ca}"
        result += ",pathlen:#{self.path_len}" unless self.path_len.nil?
        result
      end
    end

    class CrlDistributionPoints
      include ExtensionAPI

      attr_accessor :uri

      def initialize
        # self.uri = "http://moo.crlendPoint.example.com/something.crl"
      end

      def openssl_identifier
        "crlDistributionPoints"
      end

      ## NB: At this time it seems OpenSSL's extension handlers don't support
      ## any of the config options the docs claim to support... everything comes back
      ## "missing value" on GENERAL NAME. Even if copied verbatim
      def config_extensions
        {
          # "custom_crl_fields" => {"fullname" => "URI:#{fullname}"},
          # "issuer_sect" => {"CN" => "crlissuer.com", "C" => "US", "O" => "shudder"}
        }
      end

      def to_s
        return "" if self.uri.nil?
        "URI:#{self.uri}"
      end
    end

    class SubjectKeyIdentifier
      include ExtensionAPI
      def openssl_identifier
        "subjectKeyIdentifier"
      end

      def to_s
        "hash"
      end
    end

    class AuthorityKeyIdentifier
      include ExtensionAPI

      def openssl_identifier
        "authorityKeyIdentifier"
      end

      def to_s
        "keyid,issuer"
      end
    end

    class AuthorityInfoAccess
      include ExtensionAPI

      attr_accessor :ocsp

      def initialize
        self.ocsp = []
      end

      def openssl_identifier
        "authorityInfoAccess"
      end

      def to_s
        return "" if self.ocsp.empty?
        "OCSP;URI:#{self.ocsp}"
      end
    end

    class KeyUsage
      include ExtensionAPI

      attr_accessor :usage

      def initialize
        self.usage = ["digitalSignature", "nonRepudiation"]
      end

      def openssl_identifier
        "keyUsage"
      end

      def to_s
        "#{self.usage.join(',')}"
      end
    end

    class ExtendedKeyUsage
      include ExtensionAPI

      attr_accessor :usage

      def initialize
        self.usage = ["serverAuth","clientAuth"]
      end

      def openssl_identifier
        "extendedKeyUsage"
      end

      def to_s
        "#{self.usage.join(',')}"
      end
    end

    class SubjectAlternativeName
      include ExtensionAPI

      attr_accessor :uris, :dns_names, :ips

      def initialize
        self.uris = []
        self.dns_names = []
        self.ips = []
      end

      def uris=(value)
        raise "URIs must be an array" unless value.is_a?(Array)
        @uris = value
      end

      def dns_names=(value)
        raise "DNS names must be an array" unless value.is_a?(Array)
        @dns_names = value
      end

      def ips=(value)
        raise "IPs must be an array" unless value.is_a?(Array)
        @ips = value
      end

      def openssl_identifier
        "subjectAltName"
      end

      def to_s
        res =  self.uris.map {|u| "URI:#{u}" }
        res += self.dns_names.map {|d| "DNS:#{d}" }
        res += self.ips.map {|i| "IP:#{i}" }

        return res.join(',')
      end
    end

    class CertificatePolicies
      include ExtensionAPI

      attr_accessor :policy_identifier
      attr_accessor :cps_uris
      ##User notice
      attr_accessor :explicit_text
      attr_accessor :organization
      attr_accessor :notice_numbers

      def initialize
        @contains_data = false
      end


      def openssl_identifier
        "certificatePolicies"
      end

      def user_notice=(value={})
        value.keys.each do |key|
          self.send("#{key}=".to_sym, value[key])
        end
      end

      def config_extensions
        config_extension = {}
        custom_policies = {}
        notice = {}
        unless self.policy_identifier.nil?
          custom_policies["policyIdentifier"] = self.policy_identifier
        end

        if !self.cps_uris.nil? and self.cps_uris.is_a?(Array)
          self.cps_uris.each_with_index do |cps_uri,i|
            custom_policies["CPS.#{i}"] = cps_uri
          end
        end

        unless self.explicit_text.nil?
          notice["explicitText"] = self.explicit_text
        end

        unless self.organization.nil?
          notice["organization"] = self.organization
        end

        unless self.notice_numbers.nil?
          notice["noticeNumbers"] = self.notice_numbers
        end

        if notice.keys.size > 0
          custom_policies["userNotice.1"] = "@notice"
          config_extension["notice"] = notice
        end

        if custom_policies.keys.size > 0
          config_extension["custom_policies"] = custom_policies
          @contains_data = true
        end

        config_extension
      end

      def to_s
        return "" unless @contains_data
        "ia5org,@custom_policies"
      end
    end

  end
end
