// iKSCrash.m
// B4i wrapper implementation for KSCrash 2.x
//
// Compile this file as part of your Xcode static-library target alongside
// the KSCrash SPM or CocoaPods products.

#import "iKSCrash.h"
#import <B4i/B4i.h>                       // B4i runtime headers from the Mac server

// KSCrash 2.x public API headers
#import <KSCrashRecording/KSCrash.h>
#import <KSCrashRecording/KSCrashConfiguration.h>
#import <KSCrashRecording/KSCrashMonitorType.h>
#import <KSCrashInstallations/CrashInstallationStandard.h>
#import <KSCrashInstallations/CrashInstallationConsole.h>

// ---------------------------------------------------------------------------
// Internal helper: parse a B4i comma-separated monitor string -> KSCrashMonitorType
// ---------------------------------------------------------------------------
static KSCrashMonitorType MonitorsFromString(NSString *monitors) {
    KSCrashMonitorType result = KSCrashMonitorTypeNone;
    NSArray<NSString *> *parts = [monitors componentsSeparatedByString:@","];
    for (NSString *part in parts) {
        NSString *trimmed = [part stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
        if ([trimmed isEqualToString:@"machException"])    result |= KSCrashMonitorTypeMachException;
        else if ([trimmed isEqualToString:@"signal"])      result |= KSCrashMonitorTypeSignal;
        else if ([trimmed isEqualToString:@"cppException"])result |= KSCrashMonitorTypeCPPException;
        else if ([trimmed isEqualToString:@"objcException"])result |= KSCrashMonitorTypeNSException;
        else if ([trimmed isEqualToString:@"mainThreadDeadlock"]) result |= KSCrashMonitorTypeMainThreadDeadlock;
        else if ([trimmed isEqualToString:@"zombie"])      result |= KSCrashMonitorTypeZombie;
    }
    return result;
}

// ---------------------------------------------------------------------------
@implementation B4iKSCrashInstallation {
    CrashInstallationStandard *_installation;
    NSMutableDictionary<NSString *, NSString *> *_userInfo;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        _installation = CrashInstallationStandard.shared;
        _userInfo = [NSMutableDictionary dictionary];
    }
    return self;
}

// ---------------------------------------------------------------------------
- (void)Install {
    KSCrashConfiguration *cfg = [KSCrashConfiguration new];
    cfg.monitors = KSCrashMonitorTypeMachException | KSCrashMonitorTypeSignal;
    NSError *error = nil;
    [_installation installWithConfiguration:cfg error:&error];
    if (error) {
        NSLog(@"[iKSCrash] Install error: %@", error.localizedDescription);
    }
}

// ---------------------------------------------------------------------------
- (void)InstallWithMonitors:(NSString *)monitors {
    KSCrashConfiguration *cfg = [KSCrashConfiguration new];
    cfg.monitors = MonitorsFromString(monitors);
    NSError *error = nil;
    [_installation installWithConfiguration:cfg error:&error];
    if (error) {
        NSLog(@"[iKSCrash] InstallWithMonitors error: %@", error.localizedDescription);
    }
}

// ---------------------------------------------------------------------------
- (void)SetReportUrl:(NSString *)reportUrl {
    _installation.url = [NSURL URLWithString:reportUrl];
}

// ---------------------------------------------------------------------------
- (void)SendAllReports:(NSString *)EventName {
    // Capture the B4i event owner (the module that called this method).
    // B4i passes the calling object as the first implicit parameter via B4I*.
    // We store it before the async block so the event can be raised later.
    __weak B4iKSCrashInstallation *weakSelf = self;
    NSString *evtName = EventName;

    [_installation sendAllReportsWithCompletion:^(NSArray *reports, BOOL completed, NSError *error) {
        B4iKSCrashInstallation *strongSelf = weakSelf;
        if (!strongSelf) return;
        BOOL success = completed && (error == nil);
        int count = (int)(reports ? reports.count : 0);
        // Raise event back to B4i on the main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            [B4IObjectWrapper raiseEvent:strongSelf
                                        :[NSString stringWithFormat:@"_%@:", [evtName lowercaseString]]
                                        :@[@(success), @(count)]];
        });
    }];
}

// ---------------------------------------------------------------------------
- (void)PrintAllReportsToConsole {
    CrashInstallationConsole *console = CrashInstallationConsole.shared;
    console.printAppleFormat = YES;
    [console sendAllReportsWithCompletion:nil];
}

// ---------------------------------------------------------------------------
- (void)DeleteAllReports {
    [[KSCrash sharedInstance] deleteAllReports];
}

// ---------------------------------------------------------------------------
- (void)SetUserInfoKey:(NSString *)key Value:(NSString *)value {
    _userInfo[key] = value;
    [KSCrash sharedInstance].userInfo = [_userInfo copy];
}

// ---------------------------------------------------------------------------
- (void)RemoveUserInfoKey:(NSString *)key {
    [_userInfo removeObjectForKey:key];
    [KSCrash sharedInstance].userInfo = [_userInfo copy];
}

// ---------------------------------------------------------------------------
- (void)ReportCustomException:(NSString *)name
                       Reason:(NSString *)reason
                     Language:(NSString *)language
                   LineOfCode:(NSString *)lineOfCode
                   StackTrace:(NSString *)stackTrace
                 TerminateApp:(BOOL)terminateApp {
    // Build a simple array-of-dicts stack trace from newline-separated input
    NSArray<NSString *> *lines = [stackTrace componentsSeparatedByString:@"\n"];
    NSMutableArray<NSDictionary *> *frames = [NSMutableArray array];
    for (NSString *line in lines) {
        NSString *trimmed = [line stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceCharacterSet];
        if (trimmed.length > 0) {
            [frames addObject:@{@"content": trimmed}];
        }
    }

    [[KSCrash sharedInstance] reportUserException:name
                                           reason:reason
                                         language:language
                                       lineOfCode:lineOfCode
                                       stackTrace:frames
                                 logAllThreads:NO
                               terminateProgram:terminateApp];
}

// ---------------------------------------------------------------------------
- (BOOL)DidCrashLastLaunch {
    return [KSCrash sharedInstance].crashedLastLaunch;
}

// ---------------------------------------------------------------------------
- (int)PendingReportCount {
    return (int)[[KSCrash sharedInstance] reportIDs].count;
}

// ---------------------------------------------------------------------------
- (void)SetIntrospectMemory:(BOOL)enabled {
    [KSCrash sharedInstance].introspectMemory = enabled;
}

// ---------------------------------------------------------------------------
- (void)SetZombieTrackingEnabled:(BOOL)enabled {
    // Zombie monitoring is toggled via the active monitor mask at runtime.
    KSCrash *ks = [KSCrash sharedInstance];
    if (enabled) {
        ks.monitoring = ks.monitoring | KSCrashMonitorTypeZombie;
    } else {
        ks.monitoring = ks.monitoring & ~KSCrashMonitorTypeZombie;
    }
}

@end
