//  KeychainWrapper.h
//
// Copyright (c) 2014 Auth0 (http://auth0.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#import <Foundation/Foundation.h>

///---------------------------------------------------
/// @name Keychain Items Accessibility Values
///---------------------------------------------------

/**
 *  Enum with Kechain items accessibility types. It's a mirror of `kSecAttrAccessible` values.
 */
typedef NS_ENUM(NSInteger, KeychainWrapperItemAccessible) {
    /**
     *  @see kSecAttrAccessibleWhenUnlocked
     */
    KeychainWrapperItemAccessibleWhenUnlocked = 0,
    /**
     *  @see kSecAttrAccessibleAfterFirstUnlock
     */
    KeychainWrapperItemAccessibleAfterFirstUnlock,
    /**
     *  @see kSecAttrAccessibleAlways
     */
    KeychainWrapperItemAccessibleAlways,
    /**
     *  @see kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
     */
    KeychainWrapperItemAccessibleWhenPasscodeSetThisDeviceOnly,
    /**
     *  @see kSecAttrAccessibleWhenUnlockedThisDeviceOnly
     */
    KeychainWrapperItemAccessibleWhenUnlockedThisDeviceOnly,
    /**
     *  kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
     */
    KeychainWrapperItemAccessibleAfterFirstUnlockThisDeviceOnly,
    /**
     *  @see kSecAttrAccessibleAlwaysThisDeviceOnly
     */
    KeychainWrapperItemAccessibleAlwaysThisDeviceOnly
};

#define A0ErrorDomain @"com.auth0.simplekeychain"

/**
 * Enum with keychain error codes. It's a mirror of the keychain error codes. 
 */
typedef NS_ENUM(NSInteger, KeychainWrapperError) {
    /**
     * @see errSecSuccess
     */
    KeychainWrapperErrorNoError = 0,
    /**
     * @see errSecUnimplemented
     */
    KeychainWrapperErrorUnimplemented = -4,
    /**
     * @see errSecParam
     */
    KeychainWrapperErrorWrongParameter = -50,
    /**
     * @see errSecAllocate
     */
    KeychainWrapperErrorAllocation = -108,
    /**
     * @see errSecNotAvailable
     */
    KeychainWrapperErrorNotAvailable = -25291,
    /**
     * @see errSecAuthFailed
     */
    KeychainWrapperErrorAuthFailed = -25293,
    /**
     * @see errSecDuplicateItem
     */
    KeychainWrapperErrorDuplicateItem = -25299,
    /**
     * @see errSecItemNotFound
     */
    KeychainWrapperErrorItemNotFound = -25300,
    /**
     * @see errSecInteractionNotAllowed
     */
    KeychainWrapperErrorInteractionNotAllowed = -25308,
    /**
     * @see errSecDecode
     */
    KeychainWrapperErrorDecode = -26275
};

NS_ASSUME_NONNULL_BEGIN

/**
 *  A simple helper class to deal with storing and retrieving values from iOS Keychain.
 *  It has support for sharing keychain items using Access Group and also for iOS 8 fine grained accesibility over a specific Kyechain Item (Using Access Control).
 *  The support is only available for iOS 8+, otherwise it will default using the coarse grained accesibility field.
 *  When a `NSString` or `NSData` is stored using Access Control and the accesibility flag `KeychainWrapperItemAccessibleWhenPasscodeSetThisDeviceOnly`, iOS will prompt the user for it's passcode or pass a TouchID challenge (if available).
 */
@interface KeychainWrapper : NSObject

/**
 *  Service name under all items are saved. Default value is Bundle Identifier.
 */
@property (readonly, nonatomic) NSString *service;

/**
 *  Access Group for Keychain item sharing. If it's nil no keychain sharing is possible. Default value is nil.
 */
@property (readonly, nullable, nonatomic) NSString *accessGroup;

/**
 *  What type of accessibility the items stored will have. All values are translated to `kSecAttrAccessible` constants.
 *  Default value is KeychainWrapperItemAccessibleAfterFirstUnlock.
 *  @see kSecAttrAccessible
 */
@property (assign, nonatomic) KeychainWrapperItemAccessible defaultAccessiblity;

/**
 *  Tells KeychainWrapper to use `kSecAttrAccessControl` instead of `kSecAttrAccessible`. It will work only in iOS 8+, defaulting to `kSecAttrAccessible` on lower version.
 *  Default value is NO.
 */
@property (assign, nonatomic) BOOL useAccessControl;

///---------------------------------------------------
/// @name Initialization
///---------------------------------------------------

/**
 *  Initialise a `KeychainWrapper` with default values.
 *
 *  @return an initialised instance
 */
- (instancetype)init;

/**
 *  Initialise a `KeychainWrapper` with a given service.
 *
 *  @param service name of the service to use to save items.
 *
 *  @return an initialised instance.
 */
- (instancetype)initWithService:(NSString *)service;

/**
 *  Initialise a `KeychainWrapper` with a given service and access group.
 *
 *  @param service name of the service to use to save items.
 *  @param accessGroup name of the access group to share items.
 *
 *  @return an initialised instance.
 */
- (instancetype)initWithService:(NSString *)service accessGroup:(nullable NSString *)accessGroup;

///---------------------------------------------------
/// @name Store values
///---------------------------------------------------

/**
 *  Saves the NSString with the type `kSecClassGenericPassword` in the keychain.
 *
 *  @param string value to save in the keychain
 *  @param key    key for the keychain entry.
 *
 *  @return if the value was saved it will return YES. Otherwise it'll return NO.
 */
- (BOOL)setString:(NSString *)string forKey:(NSString *)key;

/**
 *  Saves the NSData with the type `kSecClassGenericPassword` in the keychain.
 *
 *  @param data value to save in the keychain
 *  @param key    key for the keychain entry.
 *
 *  @return if the value was saved it will return YES. Otherwise it'll return NO.
 */
- (BOOL)setData:(NSData *)data forKey:(NSString *)key;

/**
 *  Saves the NSString with the type `kSecClassGenericPassword` in the keychain.
 *
 *  @param string   value to save in the keychain
 *  @param key      key for the keychain entry.
 *  @param message  prompt message to display for TouchID/passcode prompt if neccesary
 *
 *  @return if the value was saved it will return YES. Otherwise it'll return NO.
 */
- (BOOL)setString:(NSString *)string forKey:(NSString *)key promptMessage:(nullable NSString *)message;

/**
 *  Saves the NSData with the type `kSecClassGenericPassword` in the keychain.
 *
 *  @param data   value to save in the keychain
 *  @param key      key for the keychain entry.
 *  @param message  prompt message to display for TouchID/passcode prompt if neccesary
 *
 *  @return if the value was saved it will return YES. Otherwise it'll return NO.
 */
- (BOOL)setData:(NSData *)data forKey:(NSString *)key promptMessage:(nullable NSString *)message;

///---------------------------------------------------
/// @name Remove values
///---------------------------------------------------

/**
 *  Removes an entry from the Keychain using its key
 *
 *  @param key the key of the entry to delete.
 *
 *  @return If the entry was removed it will return YES. Otherwise it will return NO.
 */
- (BOOL)deleteEntryForKey:(NSString *)key;

/**
 *  Remove all entries from the kechain with the service and access group values.
 */
- (void)clearAll;

///---------------------------------------------------
/// @name Obtain values
///---------------------------------------------------

/**
 *  Fetches a NSString from the keychain
 *
 *  @param key the key of the value to fetch
 *
 *  @return the value or nil if an error occurs.
 */
- (nullable NSString *)stringForKey:(NSString *)key;

/**
 *  Fetches a NSData from the keychain
 *
 *  @param key the key of the value to fetch
 *
 *  @return the value or nil if an error occurs.
 */
- (nullable NSData *)dataForKey:(NSString *)key;

/**
 *  Fetches a NSString from the keychain
 *
 *  @param key     the key of the value to fetch
 *  @param message prompt message to display for TouchID/passcode prompt if neccesary
 *
 *  @return the value or nil if an error occurs.
 */
- (nullable NSString *)stringForKey:(NSString *)key promptMessage:(nullable NSString *)message;

/**
 *  Fetches a NSData from the keychain
 *
 *  @param key     the key of the value to fetch
 *  @param message prompt message to display for TouchID/passcode prompt if neccesary
 *
 *  @return the value or nil if an error occurs.
 */
- (nullable NSData *)dataForKey:(NSString *)key promptMessage:(nullable NSString *)message;

/**
 *  Fetches a NSData from the keychain
 *
 *  @param key     the key of the value to fetch
 *  @param message prompt message to display for TouchID/passcode prompt if neccesary
 *  @param err     Returns an error, if the item cannot be retrieved. F.e. item not found 
 *                 or user authentication failed in TouchId case.
 *
 *  @return the value or nil if an error occurs.
 */
- (nullable NSData *)dataForKey:(NSString *)key promptMessage:(nullable NSString *)message error:(NSError **)err;

/**
 *  Checks if a key has a value in the Keychain
 *
 *  @param key the key to check if it has a value
 *
 *  @return if the key has an associated value in the Keychain or not.
 */
- (BOOL)hasValueForKey:(NSString *)key;

///---------------------------------------------------
/// @name Create helper methods
///---------------------------------------------------

/**
 *  Creates a new instance of `KeychainWrapper`
 *
 *  @return a new instance
 */
+ (KeychainWrapper *)keychain;

/**
 *  Creates a new instance of `KeychainWrapper` with a service name.
 *
 *  @param service name of the service under all items will be stored.
 *
 *  @return a new instance
 */
+ (KeychainWrapper *)keychainWithService:(NSString *)service;

/**
 *  Creates a new instance of `KeychainWrapper` with a service name and access group
 *
 *  @param service     name of the service under all items will be stored.
 *  @param accessGroup name of the access group to share keychain items.
 *
 *  @return a new instance
 */
+ (KeychainWrapper *)keychainWithService:(NSString *)service accessGroup:(NSString *)accessGroup;

@end

NS_ASSUME_NONNULL_END
