module Fastlane
  module Actions
    module SharedValues
      
    end

    class UpdateProjectProvisioningAction < Action
      ROOT_CERTIFICATE_URL = "http://www.apple.com/appleca/AppleIncRootCertificate.cer"
      def self.run(params)
        
        # assign folder from parameter or search for xcodeproj file
        folder = params[:xcodeproj] || Dir["*.xcodeproj"].first
        
        # validate folder
        folder = File.join(folder, "project.pbxproj")
        raise "Could not find path to project config '#{folder}'. Pass the path to your project (not workspace)!".red unless File.exists?(folder)

        # download certificate
        if not File.exists?(params[:certificate])
          Helper.log.info("Downloading root certificate from (#{ROOT_CERTIFICATE_URL}) to path '#{params[:certificate]}'")
          require 'open-uri'
          File.open(params[:certificate], "w") do |file|
            file.write(open(ROOT_CERTIFICATE_URL, "rb").read)
          end
        end

        # parsing mobileprovision file
        Helper.log.info("Parsing mobile provisioning profile from '#{params[:profile]}'")
        profile = File.read(params[:profile])
        p7 = OpenSSL::PKCS7.new(profile)
        store = OpenSSL::X509::Store.new
        raise "Could not find valid certificate at '#{params[:certificate]}'" unless (File.size(params[:certificate]) > 0)
        cert = OpenSSL::X509::Certificate.new(File.read(params[:certificate]))
        store.add_cert(cert)
        verification = p7.verify([cert], store)
        data = Plist::parse_xml(p7.data)
        
        filter = params[:build_configuration_filter]

        # manipulate project file
        Helper.log.info("Going to update project '#{folder}' with UUID".green)
        require 'pbxplorer'

        project_file = XCProjectFile.new(folder)
        project_file.project.targets.each do |target|
          if filter
            if target['productName'].match(filter) or target['productType'].match(filter)
              Helper.log.info "Updating target #{target['productName']}...".green
            else
              Helper.log.info "Skipping target #{target['productName']} as it doesn't match the filter '#{filter}'".yellow
              next
            end
          else
            Helper.log.info "Updating target #{target['productName']}...".green
          end

          target.build_configuration_list.build_configurations.each do |build_configuration|
            build_configuration["buildSettings"]["PROVISIONING_PROFILE"] = data["UUID"]
            build_configuration["buildSettings"]["CODE_SIGN_RESOURCE_RULES_PATH[sdk=*]"] = "$(SDKROOT)/ResourceRules.plist"
          end
        end

        project_file.save

        # complete
        Helper.log.info("Successfully updated project settings in'#{params[:xcodeproj]}'".green)
      end

      def self.description
        "Update projects code signing settings from your profisioning profile"
      end

      def self.details
        [
          "This action retrieves a provisioning profile UUID from a provisioning profile (.mobileprovision) to set",
          "up the xcode projects' code signing settings in *.xcodeproj/project.pbxproj",
          "",
          "The `build_configuration_filter` value can be used to only update code signing for one target",
          "Example Usage is the WatchKit Extension or WatchKit App, where you need separate provisioning profiles",
          "Example: `update_project_provisioning(xcodeproj: \"..\", build_configuration_filter: \".*WatchKit App.*\")"
        ].join("\n")
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :xcodeproj,
                                       env_name: "FL_PROJECT_PROVISIONING_PROJECT_PATH",
                                       description: "Path to your Xcode project",
                                       optional: true,
                                       verify_block: Proc.new do |value|
                                        raise "Path to xcode project is invalid".red unless File.exists?(value)
                                       end),
          FastlaneCore::ConfigItem.new(key: :profile,
                                       env_name: "FL_PROJECT_PROVISIONING_PROFILE_FILE",
                                       description: "Path to provisioning profile (.mobileprovision)",
                                       default_value: Actions.lane_context[SharedValues::SIGH_PROFILE_PATH],
                                       verify_block: Proc.new do |value|
                                        raise "Path to provisioning profile is invalid".red unless File.exists?(value)
                                       end),
          FastlaneCore::ConfigItem.new(key: :build_configuration_filter,
                                       env_name: "FL_PROJECT_PROVISIONING_PROFILE_FILTER",
                                       description: "A filter for the target name. Use a standard regex",
                                       optional: true),
          FastlaneCore::ConfigItem.new(key: :certificate,
                                       env_name: "FL_PROJECT_PROVISIONING_CERTIFICATE_PATH",
                                       description: "Path to apple root certificate",
                                       default_value: "/tmp/AppleIncRootCertificate.cer")
        ]
      end

      def self.author
        "tobiasstrebitzer"
      end

      def self.is_supported?(platform)
        platform == :ios
      end
    end
  end
end