module Deliver
  class App
    attr_accessor :apple_id, :app_identifier, :metadata


    # Defines the different states of the app
    # 
    # As specified by Apple: https://developer.apple.com/library/ios/documentation/LanguagesUtilities/Conceptual/iTunesConnect_Guide/Chapters/ChangingAppStatus.html
    module AppStatus
      PREPARE_FOR_SUBMISSION = "Prepare for Submission"
      WAITING_FOR_REVIEW = "Waiting For Review"
      IN_REVIEW = "In Review"
      UPLOAD_RECEIVED = "Upload Received"
      PENDING_DEVELOPER_RELEASE = "Pending Developer Release"
      PROCESSING_FOR_APP_STORE = "Processing for App Store"
      READY_FOR_SALE = "Ready for Sale"
      REJECTED = "Rejected"


      # Unused app states
      # PENDING_APPLE_RELASE="Pending Apple Release"
      # PENDING_CONTRACT = "Pending Contract"
      # WAITING_FOR_EXPORT_COMPLIANCE = "Waiting For Export Compliance"
      # METADATA_REJECTED = "Metadata Rejected"
      # REMOVED_FROM_SALE = "Removed From Sale"
      # DEVELOPER_REJECTED = "Developer Rejected" # equals PREPARE_FOR_SUBMISSION
      # DEVELOPER_REMOVED_FROM_SALE = "Developer Removed From Sale"
      # INVALID_BINARY = "Invalid Binary"
    end

    # @param apple_id The Apple ID of the app you want to modify or update. This ID has usually 9 digits
    # @param app_identifier If you don't pass this, it will automatically be fetched from the Apple API
    #   which means it takes longer. If you **can** pass the app_identifier (e.g. com.facebook.Facebook) do it
    def initialize(apple_id: nil, app_identifier: nil)
      self.apple_id = (apple_id || '').to_s.gsub('id', '').to_i
      self.app_identifier = app_identifier
      
      if apple_id and not app_identifier
        # Fetch the app identifier based on the given Apple ID
        self.app_identifier = FastlaneCore::ItunesSearchApi.fetch_bundle_identifier(apple_id)
      elsif app_identifier and not apple_id
        # Fetch the Apple ID based on the given app identifier
        begin
          begin
            self.apple_id = FastlaneCore::ItunesSearchApi.fetch_by_identifier(app_identifier)['trackId']
          rescue
            Helper.log.warn "App doesn't seem to be in the App Store yet or is not available in the US App Store. Using the iTC API instead."
            # Use the iTunes Connect API instead: make that default in the future
            self.apple_id = FastlaneCore::ItunesConnect.new.find_apple_id(app_identifier)
            raise "Couldn't find Apple ID" unless self.apple_id
          end
        rescue
          unless Helper.is_test?
            Helper.log.info "Could not find Apple ID based on the app identifier in the US App Store. Maybe the app is not yet in the store?".yellow
            Helper.log.info "You can provide the Apple ID of your app using `apple_id '974739333'` in your `Deliverfile`".green

            while ((self.apple_id || '').to_s.length == 0) || ((self.apple_id || 0).to_i == 0)
              self.apple_id = ask("\nApple ID of your app (e.g. 284882215): ")
            end
          else
            raise "Please pass a valid Apple ID using 'apple_id'".red
          end
        end
      end
    end

    def to_s
      "#{apple_id} - #{app_identifier}"
    end

    #####################################################
    # @!group Interacting with iTunesConnect
    #####################################################

    # The iTC handler which is used to interact with the iTunesConnect backend
    def itc
      @itc ||= Deliver::ItunesConnect.new
    end

    # This method fetches the current app status from iTunesConnect.
    # This method may take some time to execute, since it uses frontend scripting under the hood.
    # @return the current App Status defined at {Deliver::App::AppStatus}, like "Waiting For Review"
    def get_app_status
      itc.get_app_status(self)
    end

    # This method fetches the app version of the latest published version
    # This method may take some time to execute, since it uses frontend scripting under the hood.
    # @return the currently active app version, which in production
    def get_live_version
      itc.get_live_version(self)
    end


    #####################################################
    # @!group Updating the App Metadata
    #####################################################

    # Use this method to change the default download location for the metadata packages
    def set_metadata_directory(dir)
      raise "Can not change metadata directory after accessing metadata of an app" if @metadata
      @metadata_dir = dir
    end

    # @return the path to the directy in which the itmsp files will be downloaded
    def get_metadata_directory
      return @metadata_dir if @metadata_dir 
      return "./spec/fixtures/packages/" if Helper.is_test?
      return "./"
    end

    # Access to update the metadata of this app
    # 
    # The first time accessing this, will take some time, since it's downloading
    # the latest version from iTC.
    # 
    # Don't forget to call {#upload_metadata!} once you are finished
    # @return [Deliver::AppMetadata] the latest metadata of this app
    def metadata
      @metadata ||= Deliver::AppMetadata.new(self, get_metadata_directory)
    end

    # Was the app metadata already downloaded?
    def metadata_downloaded?
      @metadata != nil
    end


    # Uploads a new app icon to iTunesConnect. This uses a headless browser
    # which makes this command quite slow.
    # @param (path) a path to the new app icon. The image must have the resolution of 1024x1024
    def upload_app_icon!(path)
      itc.upload_app_icon!(self, path)
    end

    # Uploads a new apple watch app icon to iTunesConnect. This uses a headless browser
    # which makes this command quite slow.
    # @param (path) a path to the new apple watch app icon. The image must have the resolution of 1024x1024
    def upload_apple_watch_app_icon!(path)
      itc.upload_apple_watch_app_icon!(self, path)
    end
    #####################################################
    # @!group Destructive/Constructive methods
    #####################################################

    # This method creates a new version of your app using the
    # iTunesConnect frontend. This will happen directly after calling
    # this method. 
    # @param version_number (String) the version number as string for 
    # the new version that should be created
    def create_new_version!(version_number)
      itc.create_new_version!(self, version_number)
    end

    # This method has to be called, after modifying the values of .metadata.
    # It will take care of uploading all changes to Apple.
    # This method might take a few minutes to run
    # @return [bool] true on success
    # @raise [Deliver::TransporterTransferError]
    # @raise [Deliver::TransporterInputError]
    def upload_metadata!
      raise "You first have to modify the metadata using app.metadata.setDescription" unless @metadata
      
      self.metadata.upload!
    end
  end
end