//
//  FaceVerifyClient.m
//  FaceVerify
//
//  Created by zhuxun.lx on 2022/11/7.
//

#import "FaceVerifyClient.h"
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonHMAC.h>

/**
  以下代码是去请求服务端接口，这里只是为了端上演示完整流程，所以将代码写在了iOS端，真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上，会有泄漏风险，建议将请求服务端接口代码写到您的服务端
 */
/**
  "YOUR_ACCESS_KEY_ID", "YOUR_ACCESS_KEY_SECRET" 的生成请参考https://help.aliyun.com/document_detail/175144.html
  如果您是用的子账号AccessKey，还需要为子账号授予权限AliyunVIAPIFullAccess，请参考https://help.aliyun.com/document_detail/145025.html
 */
const NSString* ACCESS_KEY_ID = @"YOUR_ACCESS_KEY_ID";
const NSString* ACCESS_KEY_SECRET = @"YOUR_ACCESS_KEY_SECRET";

@implementation FaceVerifyClient

/**
 调用GenRealPersonVerificationToken接口获取VerificationToken，可参考文档：https://help.aliyun.com/document_detail/198723.html
 这里只是为了端上演示完整流程，所以将代码写在了iOS端
 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上，会有泄漏风险，建议将请求服务端接口代码写到您的服务端
 @param certificateName 被校验的身份证人名
 @param certificateNumber 身份证号码
 @param metaInfo metainfo环境参数，需要通过客户端SDK获取
 */
+(void)genRealPersonVerificationTokenWithCertificateName:(NSString*)certificateName andCertificateNumber:(NSString*)certificateNumber andMetaInfo:(NSString*)metaInfo responseBlock:(void(^)(NSString *verificationToken, NSError *error))block{
    NSMutableDictionary *bodyDict = [NSMutableDictionary dictionary];
    bodyDict[@"AccessKeyId"] = ACCESS_KEY_ID;
    bodyDict[@"Action"] = @"GenRealPersonVerificationToken";
    bodyDict[@"CertificateName"] = certificateName;
    bodyDict[@"CertificateNumber"] = certificateNumber;
    bodyDict[@"MetaInfo"] = metaInfo;
    //验签
    NSString *finalUrl = [self allKeysGotoSignatureWithDict:bodyDict endpoint:@"facebody.cn-shanghai.aliyuncs.com" accessSecret:ACCESS_KEY_SECRET httpMethod:@"POST" apiVersion:@"2019-12-30"];
    //直接发post请求
    [self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                block(nil,error);
            }else{
                NSString *token = [responseObject objectForKey:@"VerificationToken"];
                block(token,nil);
            }
        });
    }];
}

/**
 * 调用GetRealPersonVerificationResult接口获取认证结果，可参考文档：https://help.aliyun.com/document_detail/198724.html
 * 这里只是为了端上演示完整流程，所以将代码写在了iOS端
 * 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上，会有泄漏风险，建议将请求服务端接口代码写到您的服务端
 * @param verificationToken
 */
+(int)getRealPersonVerificationResultWithVerificationToken:(NSString*)verificationToken responseBlock:(void(^)(BOOL isPassed, NSError *error))block{
    NSMutableDictionary *bodyDict = [NSMutableDictionary dictionary];
    bodyDict[@"AccessKeyId"] = ACCESS_KEY_ID;
    bodyDict[@"Action"] = @"GetRealPersonVerificationResult";
    bodyDict[@"VerificationToken"] = verificationToken;
    // 验签
    NSString *finalUrl = [self allKeysGotoSignatureWithDict:bodyDict endpoint:@"facebody.cn-shanghai.aliyuncs.com" accessSecret:ACCESS_KEY_SECRET httpMethod:@"POST" apiVersion:@"2019-12-30"];
    // 直接发post请求
    [self request:finalUrl responseBlock:^(NSDictionary *responseObject, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (error) {
                block(nil,error);
            }else{
                BOOL isPassed = [responseObject objectForKey:@"Passed"];
                block(isPassed,nil);
            }
        });
    }];
    return 0;
}

/**
 * ========================================================================================================================
 * 以下代码仅仅为了调用服务端接口计算签名，其逻辑可参考文档：https://help.aliyun.com/document_detail/144904.html
 * 这里只是为了端上演示完整流程，所以将代码写在了iOS端
 * 真正上线不建议将ACCESS_KEY_ID和ACCESS_KEY_SECRET写在端上，会有泄漏风险，建议将请求服务端接口代码写到您的服务端
 * ========================================================================================================================
 */

+(NSString*)allKeysGotoSignatureWithDict:(NSMutableDictionary*)bodyDict endpoint:(NSString*)endpoint accessSecret:(NSString*)accessSecret httpMethod:(NSString*)httpMethod apiVersion:(NSString*)apiVersion{
    // 1. 系统参数
    bodyDict[@"SignatureMethod"] = @"HMAC-SHA1";
    bodyDict[@"SignatureNonce"] = [self getNonce];
    bodyDict[@"SignatureVersion"] = @"1.0";
    bodyDict[@"Timestamp"] = [self getTimestamp];
    bodyDict[@"Format"] = @"JSON";
    // 2. 业务API参数
    bodyDict[@"RegionId"] = @"cn-shanghai";
    bodyDict[@"Version"] = apiVersion;
    //key升序排序
    NSString *sortedQueryString = [self bodyToFormString:bodyDict];

    NSMutableString *stringToSign = [NSMutableString string];
    [stringToSign appendString:[NSString stringWithFormat:@"%@&",httpMethod]];
    [stringToSign appendString:[NSString stringWithFormat:@"%@&",[self urlEncode:@"/"]]];
    [stringToSign appendString:[self urlEncode:sortedQueryString]];
    //hmacsha1 加签
    NSString *sign = [self hmacSha1:[NSString stringWithFormat:@"%@&",accessSecret] data:stringToSign];
    // 6. 签名最后也要做特殊URL编码
    NSString *signature = [self urlEncode:sign];
    //最后结果
    NSString *finalUrl = [NSString stringWithFormat:@"http://%@/?Signature=%@&%@",endpoint,signature,sortedQueryString];
    NSLog(@"finalUrl:%@",finalUrl);
    return finalUrl;
}

+(NSString*)hmacSha1:(NSString*)key data:(NSString*)data{
    const char *cKey  = [key cStringUsingEncoding:NSUTF8StringEncoding];
    const char *cData = [data cStringUsingEncoding:NSUTF8StringEncoding];
    //sha1
    unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC
                                          length:sizeof(cHMAC)];
    NSString *hash = [HMAC base64EncodedStringWithOptions:0];//将加密结果进行一次BASE64编码。
    return hash;
}

+(NSString*)getNonce{
    NSTimeInterval timestamp = [[NSDate date]timeIntervalSince1970];
    NSString *string = [NSString stringWithFormat:@"%f%@",timestamp, [[NSUUID UUID]UUIDString]];
    NSString*md5 = [self md5String:string];
    return md5;
}

// md5
+ (NSString *)md5String:(NSString *)string{
    const char *cStr = [string UTF8String];
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
    return [NSString stringWithFormat:
                @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
    ];
}

+(NSString*)getTimestamp {
    NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
    formatter.timeZone = [NSTimeZone timeZoneWithName:@"GMT"];
    formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss'Z'";
    return [formatter stringFromDate:[NSDate date]];
}

+(NSString*)bodyToFormString:(NSMutableDictionary*)query{
    NSString* url = @"";
    if (query != nil && query.count > 0) {
        NSArray *keys = query.allKeys;
        NSArray*sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1,id obj2) {
            return[obj1 compare:obj2 options:NSNumericSearch];//正序排列
        }];
        NSMutableArray *arr = [NSMutableArray array];
        for (NSString *key in sortedArray) {
            NSString *value = query[key];
            if (value.length==0) {
                continue;
            }
            NSString *key2 = [NSString stringWithFormat:@"%@=%@",[self urlEncode:key],[self urlEncode:value]];
            [arr addObject:key2];
        }
        if(arr.count > 0) {
            url = [arr componentsJoinedByString:@"&"];
        }
    }
    return url;
}

+(NSString*)urlEncode:(NSString*)value{
    NSString *unreserved = @"*-._";
    NSMutableCharacterSet *allowedCharacterSet = [NSMutableCharacterSet alphanumericCharacterSet];
    [allowedCharacterSet addCharactersInString:unreserved];
    [allowedCharacterSet addCharactersInString:@" "];
    NSString *encoded = [value stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
    encoded = [encoded stringByReplacingOccurrencesOfString:@" " withString:@"%20"];
    encoded = [encoded stringByReplacingOccurrencesOfString:@"+" withString:@"%20"];
    encoded = [encoded stringByReplacingOccurrencesOfString:@"*" withString:@"%2A"];
    encoded = [encoded stringByReplacingOccurrencesOfString:@"%7E" withString:@"~"];
    return encoded ;
}

+(void)request:(NSString*)request responseBlock:(void(^)(NSDictionary *responseObject, NSError *error))block{
    NSMutableURLRequest *msRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:request]];
    [msRequest setHTTPMethod:@"POST"];
    msRequest.timeoutInterval = 60;
    [msRequest addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
    NSURLSession *session = [NSURLSession sharedSession];
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:msRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
        NSError *parseError = nil;
        if(httpResponse.statusCode == 200){
            NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&parseError];
            NSInteger statusCode = [[responseDictionary objectForKey:@"Success"] integerValue];
            NSLog(@"response:%@",responseDictionary);
            if (statusCode == 1) {
                block([responseDictionary objectForKey:@"Data"],nil);
            }else{
                NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                NSLog(@"errorDict:%@",errorDict);
                NSString *codevalue = [errorDict objectForKey:@"Code"];
                NSString *msgvalue = [errorDict objectForKey:@"Message"];
                NSDictionary *userInfo1 = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"code:%@,error msg:%@",codevalue,msgvalue], NSLocalizedDescriptionKey,nil];
                parseError = [[NSError alloc] initWithDomain:NSCocoaErrorDomain code:httpResponse.statusCode userInfo:userInfo1];
                block(nil,parseError);
            }
        }else{
            NSLog(@"%@,error:%@",httpResponse,error);
            if (error) {
                block(nil,error);
            }else{
                NSDictionary *errorDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                NSLog(@"errorDict:%@",errorDict);
                NSString *codevalue = [errorDict objectForKey:@"Code"];
                NSString *msgvalue = [errorDict objectForKey:@"Message"];
                NSDictionary *userInfo1 = [NSDictionary dictionaryWithObjectsAndKeys:[NSString stringWithFormat:@"code:%@,error msg:%@",codevalue,msgvalue], NSLocalizedDescriptionKey,nil];
                parseError = [[NSError alloc] initWithDomain:NSCocoaErrorDomain code:httpResponse.statusCode userInfo:userInfo1];
                block(nil,parseError);
            }
        }
    }];
    [dataTask resume];
}

@end
