//
//  HPCredentialsHelper.m
//  HotPocket
//
//  Created by Tomek Wójcik on 19/09/2025.
//

#import <Security/Security.h>

#import "HPCredentialsHelper.h"

@implementation HPCredentials

#pragma mark - HPCredentials implementation

-(id)init {
    if (self = [super init]) {
        self.baseURL = nil;
        self.accessToken = nil;
    }
    
    return self;
}

-(BOOL)usable {
    return (self.baseURL != nil && self.accessToken != nil);
}

-(NSURL *)rpcURL {
    return [NSURL URLWithString:self.baseURL];
}

-(NSString *)description {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithCapacity:2];
    
    if (self.baseURL == nil) {
        [attributes setValue:@"(null)" forKey:@"baseURL"];
    } else {
        [attributes setValue:self.baseURL forKey:@"baseURL"];
    }
    
    if (self.accessToken == nil) {
        [attributes setValue:@"(null)" forKey:@"accessToken"];
    } else {
        [attributes setValue:@"***" forKey:@"accessToken"];
    }
    
    return [NSString stringWithFormat:@"<%@: %p; %@>", NSStringFromClass([self class]), self, attributes];
}

@end

@implementation HPCredentialsHelper (HPCredentialsHelperPrivate)

#pragma mark - Private interface

-(NSString *)getService {
#ifdef DEBUG
    return @"pl.bthlabs.HotPocket.Debug";
#else
    return @"pl.bthlabs.HotPocket";
#endif
}

-(NSData *)getKeychainItem:(NSString *)service account:(NSString *)account {
    if (service == nil || account == nil) {
        return nil;
    }
    
    NSDictionary *query = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: service,
        (__bridge id)kSecAttrAccount: account,
        (__bridge id)kSecReturnData: @YES,
        (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne,
        (__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
        (__bridge id)kSecUseDataProtectionKeychain: @YES,
    };
    
    CFTypeRef resultData = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &resultData);
    
    if (status == errSecSuccess && resultData != NULL) {
        NSData *result = (__bridge_transfer NSData *)resultData;
        return result;
    } else {
        CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
        NSString *statusString = (__bridge NSString *)statusStringRef;
        NSLog(@"-[HPCredentialsHelper getKeychainItem:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
        return nil;
    }
}

-(BOOL)createKeychainItemWithValue:(NSData *)value service:(NSString *)service account:(NSString *)account {
    if (value == nil || service == nil || account == nil) {
        return NO;
    }
    
    NSDictionary *attributes = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: service,
        (__bridge id)kSecAttrAccount: account,
        (__bridge id)kSecValueData: value,
        (__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
        (__bridge id)kSecUseDataProtectionKeychain: @YES,
    };

    OSStatus status = SecItemAdd((__bridge CFDictionaryRef)attributes, NULL);
    
    if (status != errSecSuccess) {
        CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
        NSString *statusString = (__bridge NSString *)statusStringRef;
        NSLog(@"-[HPCredentialsHelper createKeychainItemWithValue:service:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
        return NO;
    }
    
    return YES;
}

-(BOOL)deleteKeychainItem:(NSString *)service account:(NSString *)account {
    if (service == nil || account == nil) {
        return NO;
    }
    
    NSDictionary *query = @{
        (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
        (__bridge id)kSecAttrService: service,
        (__bridge id)kSecAttrAccount: account,
        (__bridge id)kSecAttrAccessGroup: @"648728X64K.pl.bthlabs.HotPocketShared",
        (__bridge id)kSecUseDataProtectionKeychain: @YES,
    };
    
    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
    
    if (status != errSecSuccess) {
        CFStringRef statusStringRef = SecCopyErrorMessageString(status, NULL);
        NSString *statusString = (__bridge NSString *)statusStringRef;
        NSLog(@"-[HPCredentialsHelper deleteKeychainItem:account:] service=`%@` account=`%@` status=%@", service, account, statusString);
        return NO;
    }
    
    return YES;
}

@end

@implementation HPCredentialsHelper

#pragma mark - Initialization

+(instancetype)sharedHelper {
    static HPCredentialsHelper *sharedInstance = nil;
    static dispatch_once_t initToken;
    dispatch_once(&initToken, ^{
        sharedInstance = [[self alloc] init];
    });
    
    return sharedInstance;
}

#pragma mark - Public interface

-(HPCredentials *)getCredentials {
    HPCredentials *result = [[HPCredentials alloc] init];
    
    NSData *itemData = [self getKeychainItem:[self getService] account:@"RPC"];
    if (itemData != nil) {
        NSError *error;
        NSDictionary *itemPayload = [NSJSONSerialization JSONObjectWithData:itemData
                                                                         options:NSJSONReadingTopLevelDictionaryAssumed
                                                                           error:&error];
        
        if (error != nil) {
            NSLog(@"-[HPCredentialsHalper getCredentials] error=`%@`", error);
        } else if (itemPayload != nil) {
            result.baseURL = [itemPayload valueForKey:@"baseURL"];
            result.accessToken = [itemPayload valueForKey:@"accessToken"];
        }
    }
    
    return result;
}

-(BOOL)saveCredentials:(NSString *)baseURL accessToken:(NSString *)accessToken {
    NSMutableDictionary *itemPayload = [NSMutableDictionary dictionaryWithCapacity:2];
    
    if (baseURL != nil) {
        [itemPayload setValue:baseURL forKey:@"baseURL"];
    }
    
    if (accessToken != nil) {
        [itemPayload setValue:accessToken forKey:@"accessToken"];
    }
    
    NSError *error;
    NSData *itemData = [NSJSONSerialization dataWithJSONObject:itemPayload options:0 error:&error];
    
    if (error != nil) {
        NSLog(@"-[HPCredentialsHalper saveCredentials:accessToken:] error=`%@`", error);
        return NO;
    }
    
    BOOL saveResult = [self createKeychainItemWithValue:itemData service:[self getService] account:@"RPC"];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"HPCredentialsChanged" object:self];
    
    return saveResult;
}

-(BOOL)clearCredentials {
    BOOL deleteResult = [self deleteKeychainItem:[self getService] account:@"RPC"];
    
    [[NSNotificationCenter defaultCenter] postNotificationName:@"HPCredentialsChanged" object:self];
    
    return deleteResult;
}

@end
