require 'pathname'

require 'spaceship'
require 'fastlane_core/print_table'
require_relative 'module'

module PEM
  # Creates the push profile and stores it in the correct location
  class Manager
    class << self
      def start
        FastlaneCore::PrintTable.print_values(config: PEM.config, hide_keys: [:new_profile], title: "Summary for PEM #{Fastlane::VERSION}")
        login

        existing_certificate = certificate_sorted.detect do |c|
          c.owner_name == PEM.config[:app_identifier]
        end

        if existing_certificate
          remaining_days = (existing_certificate.expires - Time.now) / 60 / 60 / 24
          UI.message("Existing push notification profile for '#{existing_certificate.owner_name}' is valid for #{remaining_days.round} more days.")
          if remaining_days > PEM.config[:active_days_limit]
            if PEM.config[:force]
              UI.success("You already have an existing push certificate, but a new one will be created since the --force option has been set.")
            else
              UI.success("You already have a push certificate, which is active for more than #{PEM.config[:active_days_limit]} more days. No need to create a new one")
              UI.success("If you still want to create a new one, use the --force option when running PEM.")
              return false
            end
          end
        end

        return create_certificate
      end

      def login
        UI.message("Starting login with user '#{PEM.config[:username]}'")
        Spaceship.login(PEM.config[:username], nil)
        Spaceship.client.select_team
        UI.message("Successfully logged in")
      end

      def create_certificate
        UI.important("Creating a new push certificate for app '#{PEM.config[:app_identifier]}'.")

        csr, pkey = Spaceship.certificate.create_certificate_signing_request

        begin
          cert = certificate.create!(csr: csr, bundle_id: PEM.config[:app_identifier])
        rescue => ex
          if ex.to_s.include?("You already have a current")
            # That's the most common failure probably
            UI.message(ex.to_s)
            UI.user_error!("You already have 2 active push profiles for this application/environment. You'll need to revoke an old certificate to make room for a new one")
          else
            raise ex
          end
        end

        x509_certificate = cert.download

        filename_base = PEM.config[:pem_name] || "#{certificate_type}_#{PEM.config[:app_identifier]}"
        filename_base = File.basename(filename_base, ".pem") # strip off the .pem if it was provided.

        output_path = File.expand_path(PEM.config[:output_path])
        FileUtils.mkdir_p(output_path)

        if PEM.config[:save_private_key]
          private_key_path = File.join(output_path, "#{filename_base}.pkey")
          File.write(private_key_path, pkey.to_pem)
          UI.message("Private key: ".green + Pathname.new(private_key_path).realpath.to_s)
        end

        if PEM.config[:generate_p12]
          p12_cert_path = File.join(output_path, "#{filename_base}.p12")
          p12 = OpenSSL::PKCS12.create(PEM.config[:p12_password], certificate_type, pkey, x509_certificate)
          File.write(p12_cert_path, p12.to_der)
          UI.message("p12 certificate: ".green + Pathname.new(p12_cert_path).realpath.to_s)
        end

        x509_cert_path = File.join(output_path, "#{filename_base}.pem")
        File.write(x509_cert_path, x509_certificate.to_pem + pkey.to_pem)
        UI.message("PEM: ".green + Pathname.new(x509_cert_path).realpath.to_s)
        return x509_cert_path
      end

      def certificate
        if PEM.config[:development]
          Spaceship.certificate.development_push
        elsif PEM.config[:website_push]
          Spaceship.certificate.website_push
        else
          Spaceship.certificate.production_push
        end
      end

      def certificate_type
        if PEM.config[:development]
          'development'
        elsif PEM.config[:website_push]
          'website'
        else
          'production'
        end
      end

      def certificate_sorted
        certificate.all.sort { |x, y| y.expires <=> x.expires }
      end
    end
  end
end
