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

#import "HPRPCClient.h"

@implementation HPRPCCallResult

#pragma mark - HPRPCCallResult implementation

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

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

@end

@implementation HPRPCClient

#pragma mark - Initialization

-(id)initWithBaseURL:(nullable NSURL *)baseURL accessToken:(nullable NSString *)accessToken {
    if (self = [super init]) {
        self.baseURL = baseURL;
        self.accessToken = accessToken;
        self.session = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.ephemeralSessionConfiguration];
    }
    
    return self;
}

#pragma mark - Public interface

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

-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params endopoint:(nullable NSString *)endpoint completionHandler:(HPRPCClientCompletionHandler)completionHandler {
    if (self.baseURL == nil) {
        return NO;
    }
    
    if (callId == nil) {
        callId = [[NSUUID UUID] UUIDString];
    }
    
    if (endpoint == nil) {
        endpoint = @"/rpc/";
    }
    
    NSBundle *mainBundle = [NSBundle mainBundle];

    NSDictionary *payload = @{
        @"jsonrpc": @"2.0",
        @"id": callId,
        @"method": method,
        @"params": params,
    };
    
    NSError *error;
    NSData *jsonPayload = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&error];
    if (!jsonPayload) {
        NSLog(@"-[HPRPCClient call:method:params:endpoint:] Unable to serialize payload: error=`%@`", error);
        HPRPCCallResult *result = [[HPRPCCallResult alloc] init];
        result.error = error;

        if (self.delegate != nil) {
            [self.delegate rpcClientDidReceiveResult:result callId:callId];
        }

        return NO;
    }
    
    NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:self.baseURL resolvingAgainstBaseURL:NO];
    urlComponents.path = endpoint;
    urlComponents.queryItems = @[
        [NSURLQueryItem queryItemWithName:@"method" value:method],
    ];
    
    NSURL *callURL = [urlComponents URL];
#ifdef DEBUG
    NSLog(@"-[HPRPCClient call:method:params:endpoint:] callURL=`%@`", callURL.absoluteString);
#endif
    
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:callURL];
    request.HTTPMethod = @"POST";
    request.HTTPBody = jsonPayload;

    [request setValue:@"application/json;charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [request setValue:[[mainBundle infoDictionary] valueForKey:@"HPRPCClientOrigin"] forHTTPHeaderField:@"Origin"];
    
    if (self.accessToken != nil) {
        NSString *authorization = [NSString stringWithFormat:@"Bearer %@", self.accessToken];
        [request setValue:authorization forHTTPHeaderField:@"Authorization"];
    }
    
    NSString *build = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
    NSString *userAgent = [NSString stringWithFormat:@"HotPocket/%@", build];
    [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
    
    NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request
                                                 completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        HPRPCCallResult *result = [[HPRPCCallResult alloc] init];
        if (error != nil) {
            result.error = error;
        } else {
            NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
            if (httpResponse.statusCode != 200) {
                result.error = [[NSError alloc] initWithDomain:@"pl.bthlabs.HotPocket.HPRPCClient" code:-32000 userInfo:@{
                    @"callId": callId,
                    @"url": httpResponse.URL,
                    @"statusCode": [NSNumber numberWithInteger:httpResponse.statusCode],
                    @"response": response,
                }];
            } else {
                NSError *jsonDecodeError;
                NSDictionary *callResult = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingTopLevelDictionaryAssumed error:&error];
                if (jsonDecodeError != nil) {
                    result.error = jsonDecodeError;
                } else {
                    NSDictionary *rpcError = [callResult valueForKey:@"error"];
                    if (rpcError != nil) {
                        NSNumber *rpcErrorCode = [rpcError valueForKey:@"code"];
                        if (rpcErrorCode == nil) {
                            rpcErrorCode = [NSNumber numberWithInt:-32000];
                        }

                        result.error = [[NSError alloc] initWithDomain:@"pl.bthlabs.HotPocket.HPRPCClient" code:[rpcErrorCode integerValue] userInfo:rpcError];
                    } else {
                        result.result = [callResult valueForKey:@"result"];
                    }
                }
            }
        }
        
        if (completionHandler) {
            completionHandler(callId, result);
        }
    }];
    [task resume];

    return YES;
}

-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params endopoint:(nullable NSString *)endpoint {
    return [self call:callId method:method params:params endopoint:endpoint completionHandler:^(NSString *callId, HPRPCCallResult *result) {
        if (self.delegate != nil) {
            [self.delegate rpcClientDidReceiveResult:result callId:callId];
        }
    }];
}

-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params {
    return [self call:callId method:method params:params endopoint:nil];
}

-(BOOL)call:(nullable NSString *)callId method:(NSString *)method params:(NSArray *)params completionHandler:(HPRPCClientCompletionHandler)completionHandler {
    return [self call:callId method:method params:params endopoint:nil completionHandler:completionHandler];
}

@end
