public typealias RequestListener = (Bool, Error?) -> Void
public typealias FeedListener = ([Any]?, Error?) -> Void

@objc public enum PULUserGender : Int {
    case pulMale
    case pulFemale
}

@objc public enum PULPrivacyLevel : Int {
    case pulPrivacySubscribed
    case pulPrivacyUnsubscribed
}

@objcMembers
public class PULPulsateManager {
    
    /*
    * When set the unauthorizedDelegate will get callbacks when the SDK tries to open the feed for an unauthorzied user.
    * The Feed will be blocked for this user and you will get a callback to handle the action.
    * You can ask the user to for example login, enter pin, or any other authorization flow.
    * Once the user authorizes call - setUserAuthorized(true).
    * If you want to "replay" the blocked action call showLastUnauthorizedMessage() after.
    */
    @IBOutlet public weak var unauthorizedDelegate: PULPulsateUnauthorizedManagerDelegate?
    
    /*
    * When set the badgeDelegate will get callbacks when the SDK tries to change the badge count.
    * This can be used to track the badge count or set a custom badge count.
    */
    @IBOutlet public weak var badgeDelegate: PULPulsateBadgeDelegate?
    

    /**
     * Starts Pulsate session lifecycle, for the given user.
     * The first session must be started by the Developer,
     * all future sessions will be handled automatically if "logoutCurrentAlias" is NOT called.
     *
     * Starting a session in the AppDelegate is not supported.
     * Pulsate requires an active ViewController to start.
     * Wait for the first ViewController create callback before starting a session.
     *
     * Session starts when the app enters foreground and ends when it goes to background.
     * There is nothing needed to end a session, it is handled automatically.
     * Logout is not end session, do not use it to end session.
     *
     * @param alias - unique identifier in your system for this user
     */
    @objc public func startPulsateSession(_ listener: @escaping RequestListener) 

    /**
     * Starts Pulsate session lifecycle, for the given user.
     * The first session must be started by the Developer,
     * all future sessions will be handled automatically if "logoutCurrentAlias" is NOT called.
     *
     * Starting a session in the AppDelegate is not supported.
     * Pulsate requires an active ViewController to start.
     * Wait for the first ViewController create callback before starting a session.
     *
     * Session starts when the app enters foreground and ends when it goes to background.
     * There is nothing needed to end a session, it is handled automatically.
     * Logout is not end session, do not use it to end session.
     *
     * @param alias - unique identifier in your system for this user
     */
    @objc public func startPulsateSession(forAlias alias: String, withListener listener: @escaping RequestListener)

    /**
     * Logs out the currently logged in user.
     * Logout causes the current device to be detached from the user / alias.
     * This means that Pulsate will no longer be able to reach this user with any campaigns.
     * Pushes, In Apps, Feed Posts will not be delivered.
     * The user can not be segmented based on this device data.
     * This is almost like a "soft delete".
     * Use only when you want to totally logout the user from Pulsate and disable all Pulsate features.
     */
    @objc public func logout(_ listener: @escaping RequestListener)

    ///  If you chose to have location disabled when instantiating the Pulsate Manager, you can enable it later.
    ///  This enables you to postpone the location query prompt.
    @objc public func startLocation()

    ///  If you chose to have push disabled when instantiating the Pulsate Manager, you can enable it later.
    ///  This enables you to postpone the push query prompt.
    @objc public func startRemoteNotifications()

    ///  If you chose to have push disabled when instantiating the Pulsate Manager, you can enable it later.
    ///  This enables you to postpone the push query prompt and customize the push settings
    @objc public func startRemoteNotifications(with notificationSettings: UIUserNotificationSettings)

    ///  Creates and returns a Pulsate Feeed Navigation Controller. You can choose to present it however you see fit.
    @objc public func getFeedNavigationController() -> UINavigationController?

    ///  Allows Developers to get the Pulsate Inbox JSON that they can later render and show to the user in a custom way
    @objc public func getFeed(_ page: String?, with listener: @escaping FeedListener)

    ///  When using -(void)getFeed:(NSString*)page withListener:(nonnull FeedListener)listener;
    ///  We recommend using this method to pass user onClick events to Pulsate to properly handle it.
    ///  Pulsate will make sure that the click does what it should do - open url, open deeplink, send custom events, record opens / clicks
    @objc public func handleFeedClick(_ pulsateInboxItem: Any?)

    /// If feed is opened automatically (by pressing a push notification for example) you might want to
    /// hide it programatically.
    @objc public func closeAutomaticallyOpenedFeed()

    /// Developers can now add an additional button in the Pulsate Feed. The button will appear in the main Feed Toolbar in the right corner.
    /// To add this button you need to call the new setInboxRightButton method and pass an UIBarButtonItem that you want to show. This should be
    /// done in the AppDelegate under didFinishLaunchingWithOptions.
    /// - Parameter rightButton: UIBarButtonItem
    @objc public func setInboxRightButton(_ rightButton: UIBarButtonItem?)

    ///  Disables or Enables push notifications for Pulsate
    ///
    ///  - Parameter enabled: user's push notification preference
    @objc public func setPushNotificationEnabled(_ enabled: Bool)
    @objc public func isPushNotificationEnabled() -> Bool

    ///  Sets user's privacy settings.
    ///
    ///  - Parameter privacyLevel: user's privacy level
    @objc public func setPrivacy(_ privacyLevel: PULPrivacyLevel)
    @objc public func getPrivacy() -> Int

    /// Enables or Disables using the user initials as the user avatar.
    /// When disabled the avatar will always be an anon icon.
    /// By default enabled
    ///
    /// - Parameter useInitials:
    @objc public func useInitials(forUserAvatar useInitials: Bool)
    @objc public func getUseInitialsForUserAvatar() -> Bool

    /// Enables or Disables the Geofencing and Beacon Scanning.
    /// By default enabled.
    @objc public func setLocationUpdatesEnabled(_ enabled: Bool)
    @objc public func isLocationEnabled() -> Bool

    ///  Enables or Disables In-App Notifications. Default - YES.
    @objc public func enable(inAppNotification enabled: Bool)
    @objc public func isInAppNotificationEnabled() -> Bool
    
    /**
     * Sets the duration of small in app notifications. Default - 12s
     */
    @objc public func setSmallInAppNotificationDuration(_ seconds: Int)
    @objc public func getSmallInAppNotificationDuration() -> Int

    ///  Shows the last in app notification. In App Notification need to be enabled.
    ///  To enable in app notifications use enableInAppNotification(BOOL).
    @objc public func showLastInAppNotification( _ forceShow: Bool = false)

    ///  Set if user is authorized or not. The default is YES.
    @objc public func setUserAuthorized(_ authorized: Bool)
    @objc public func isUserAuthorized() -> Bool

    ///  Sets the create thread button in feed visibility.
    ///
    ///  - Parameter buttonEnabled: decides if the button should be enabled
    @objc public func setNewThreadButtonEnabled(_ buttonEnabled: Bool)
    @objc public func isNewThreadButtonEnabled() -> Bool

    ///  Updates the user's first name. Gets updated when entering background.
    ///
    ///  - Parameter firstName: firstName - can't be nil
    @objc public func updateFirstName(_ firstName: String?)

    ///  Updates the user's last name. Gets updated when entering background.
    ///
    ///  - Parameter lastName: lastName - can't be nil
    @objc public func updateLastName(_ lastName: String?)

    ///  Updates the user's email. Gets updated when entering background.
    ///
    ///  - Parameter email: email - can't be nil
    @objc public func updateEmail(_ email: String?)

    ///  Updates the user's phone number. Gets updated when entering background.
    ///  Specify the phone number using the E.164 format. E.164 is a standard for the phone number structure used for international telecommunication.
    ///  Phone numbers that follow this format can have a maximum of 15 digits, and they are prefixed with the plus character (+) and the country code.
    ///  For example, a contact in the United States has a country code "1" , area code "408" and phone number "XXX-XXXX", you'd enter +1408XXXXXXX.
    ///  - Parameter phoneNumber: - can't be nil
    @objc public func updatePhoneNumber(_ phoneNumber: String?)

    ///  Updates user's age. Gets updated when entering background.
    ///
    ///  - Parameter age: user's age
    @objc public func updateAge(_ age: Int)

    ///  Updates user's gender. Gets updated when entering background.
    ///
    ///  - Parameter gender: user's gender
    @objc public func updateGender(_ gender: PULUserGender)

    ///  Creates a custom attribute with a string. Neither can be nil. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - propertyName: custom attribute name
    ///    - value:        attribute value
    @objc public func createAttribute(_ propertyName: String?, withString value: String?)

    ///  Creates a custom attribute with a float. Key can't be nil. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - propertyName: custom attribute name
    ///    - number:       attribute value
    @objc public func createAttribute(_ propertyName: String?, withDecimal number: NSDecimalNumber?)


    ///  Creates a custom attribute with an integer. Key can't be nil. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - propertyName: custom attribute name
    ///    - number:       attribute value
    @objc public func createAttribute(_ propertyName: String?, withInteger number: Int)

    ///  Creates custom attribute with a bool. Key can't be nil. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - propertyName: custom attribute name
    ///    - boolean:      attribute value
    @objc public func createAttribute(_ propertyName: String?, withBoolean boolean: Bool)

    ///  Creates custom attribute with a date. Key can't be nil. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - propertyName: custom attribute name
    ///    - date:         attribute value
    @objc public func createAttribute(_ propertyName: String?, withDate date: Date?)

    ///  Increments given integer attribute by given value. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - attributeName: which attribute to increment
    ///    - value:         value to increment by
    @objc public func incrementIntegerAttribute(_ attributeName: String?, withInteger value: Int)

    ///  Decrements given integer attribute by given. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - attributeName: custom attribute name
    ///    - value:         attribute value
    @objc public func decrementIntegerAttribute(_ attributeName: String?, withInteger value: Int)

    ///  Increments given float attribute with given value. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - attributeName: custom attribute name
    ///    - value:         attribute value
    @objc public func incrementDecimalAttribute(_ attributeName: String?, withDecimal value: NSDecimalNumber?)

    ///  Decrements given float attribute with given value. Gets updated when entering background.
    ///
    ///  - Parameters:
    ///   - attributeName: custom attribute name
    ///    - value:         attribute value
    @objc public func decrementDecimalAttribute(_ attributeName: String?, withDecimal value: NSDecimalNumber?)

    ///  Sends a custom in app event
    ///
    ///  - Parameter event: event to be sent - can't be nil
    @objc public func createEvent(_ event: String?)
    @objc public func createEvents(_ event: [Any]?)

    ///  Sends a custom revenue event
    ///
    ///  - Parameter revenueEvent: event to be sent - can't be nil
    @objc public func createRevenueEvent(_ revenueEvent: PULRevenueEvent?)

    /**
     * Attributes synchronize when the app is entering background.
     * This method forces the synchronization to happen almost instantly.
     * This method is debounced to merge multiple calls into one.
     */
    @objc public func forceAttributeSync()

    ///  Decides if beacon actions should be sent with a location. The default is NO.
    @objc public func sendLocation(withBeaconEvents sendLocation: Bool)

    ///  Returns the Device Guid that Pulsate uses to identify users.
    @objc public func getDeviceGuid() -> String?

    ///  Sends a request to the server to get the Pulsate badge count.
    ///  The badge count will be returned in the PULPulsateBadgeDelegate badgeUpdated callback.
    @objc public func getBadgeCount()

    ///  Shows the last blocked message or card. User must be authorized.
    ///  To authorize a user use setUserAuthorized(BOOL).
    @objc public func showLastUnauthorizedMessage()
    
    ///  Shows the last in app notification. In App Notification need to be enabled.
    ///  To enable in app notifications use enableInAppNotification(BOOL).
    @objc public func getPulsateSystemManager() -> (UIApplicationDelegate & UNUserNotificationCenterDelegate)?
}