//
//  MyAdServer.m
//  nochange
//
//  Created by mac on 2024/7/26.
//

#import <Foundation/Foundation.h>
#import <signal.h>
#import <execinfo.h>

#import "XSHelper.h"
#import "MyAdServer.h"
#import "XSPhoneConfig.h"
#import "XSPhoneInfo.h"

void pushAdTaskLog(AdTaskLogData *data) {
    NSDictionary *dic = @{
        @"taskId": data->taskId,
        @"title": data->title,
        @"message": data->message,
        @"idfa": data->idfa,
        @"appid": data->appid,
        @"adid": data->adid,
        @"level": @(data->level),
        @"ecpm": data->ecpm ? : @0.0,
        @"iphoneId": data->iphoneId,
        @"ipAddr": data->ipAddr
    };
    saveAdTaskLog(dic);
}

void saveAdTaskLog(NSDictionary *dic) {
    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *url = [config GetFullServerURL:@"ios/top_selection/push_ad_task_log"];
    NSString *json = [XSHelper dic2Json:dic];
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    [http doPOST:url json:json  withCallback:^(NSData *res) {
        NSLog(@"-ad log- push:\n %@", [XSHelper data2str:res]);
    } withError:^(NSError *err) {
        NSLog(@"XS- %@", err);
    }];
}

void getLowEcpm(LowEcpmCallback callback) {
    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *url = [NSString stringWithFormat:@"ios/top_selection/low_ecpm?iphoneId=%@", config.IPhoneName];
    NSLog(@"XS- getLowEcpm url %@", url);
    NSString *fullurl = [config GetFullServerURL:url];
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    [http doGET:fullurl withCallback:^(NSData *data) {
        if(data && ![data isEqual:[NSNull null]]) {
            NSDictionary *dic = [XSHelper jsonData2Dictionary:data];
            NSLog(@"XS- getLowEcpm data %@", dic);
            NSNumber *ecpm = dic[@"data"];
            if (ecpm) {
                callback(ecpm);
            } else {
                callback(ecpm);
            }
        } else {
            callback(@(0.0001));
        }
    } withError:^(NSError *err) {
        callback(@(0.0001));
    }];
    
}

BOOL needAdContinue(NSString *appid, NSString *idfa, NSNumber *maxEcpm) {
    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *fullurl = [config GetFullServerURL: @"ios/top_selection/get_need_continue"];
    NSDictionary *postData = @{
        @"id": config.IPhoneName,
        @"appid": appid,
        @"idfa": idfa,
        @"maxEcpm": maxEcpm
    };
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    NSData *data = [http doPOST:fullurl json:[XSHelper dic2Json:postData]];
    if(data == nil || [data isEqual:[NSNull null]]) {
        return NO;
    }
    NSDictionary *dic = [XSHelper jsonData2Dictionary:data];
    NSLog(@"XS- needAdContinue data %@", dic);
    return [dic[@"data"] isEqual:@(true)];
}

void getChangeInfoWithRetry(NSString *idfa, ChangeDataSaveCallback callback, error_callback errorCallback, int retryCount) {
    if (retryCount <= 0) {
        NSLog(@"getChangeInfo failed after maximum retries");
        if (errorCallback) {
            errorCallback([NSError errorWithDomain:@"getChangeInfo" code:-1 userInfo:@{NSLocalizedDescriptionKey: @"Maximum retry attempts reached"}]);
        }
        return;
    }

    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *url = [config GetFullServerURL:@"ios/top_selection/get_change_data_json"];
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    NSDictionary *dic = @{
        @"packageName": [config PackageName],
        @"washParam": @([config WashParam]),
    };
    NSString *json = [XSHelper dic2Json:dic];

    NSLog(@"getChangeInfo: Attempting request, retries left: %d", retryCount);

    [http doPOST:url json:json withCallback:^(NSData *jsonData) {
        NSError *jsonError = nil;
        NSDictionary *data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
        if (jsonError) {
            NSLog(@"getChangeInfo: JSON parsing error: %@, retrying...", jsonError);
            // 添加延迟重试
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1);
            });
            return;
        }

        if (data && [data objectForKey:@"data"]) {
            NSDictionary *_data = data[@"data"];
            if (_data && ![_data isEqual:[NSNull null]]) {
                NSLog(@"getChangeInfo: Successfully received data");
                callback(_data);
            } else {
                NSLog(@"getChangeInfo: Invalid data received, retrying...");
                // 添加延迟重试
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1);
                });
            }
        } else {
            NSLog(@"getChangeInfo: No data received, retrying...");
            // 添加延迟重试
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1);
            });
        }
    } withError:^(NSError *err) {
        NSLog(@"getChangeInfo error: %@, retrying... (%d retries left)", err, retryCount - 1);
        // 添加延迟重试
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            getChangeInfoWithRetry(idfa, callback, errorCallback, retryCount - 1);
        });
    }];
}

// 包装函数，默认重试 3 次
void getChangeInfo(NSString *idfa, ChangeDataSaveCallback callback, error_callback errorCallback) {
    getChangeInfoWithRetry(idfa, callback, errorCallback, 3);
}

void _newgetChangeInfo(NSString *idfa, ChangeDataSaveCallback callback, error_callback errorCallback) {
    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *url = [config GetFullServerURL:[NSString stringWithFormat:@"ios/top_selection/change_data?id=%@&idfa=%@", [config IPhoneName], idfa ?: @""]];
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    [http doGET:url  withCallback:^(NSData *jsonData) {
        NSDictionary *data = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
        if (data && [data objectForKey:@"data"]) {
            NSString *aesData = data[@"data"];
            NSString *decData = @"";
            if(aesData && ![aesData isEqual:[NSNull null]] && ![aesData isEqual:@""]) {
                decData = [aesData aesDecrypt:AES_KEY iv:AES_IV];
            }
            // NSString *decData = [aesData aesDecrypt:AES_KEY iv:AES_IV];
            NSLog(@"XS- changeData: %@", decData);
            NSDictionary *dt = [XSHelper json2Dictionary:decData];
            callback(dt);
        } else {
            if(errorCallback) {
                errorCallback(nil);
            }
           
        }
    } withError:^(NSError *err) {
        NSLog(@"XS- log- get data: %@", err);
        if(errorCallback) {
            errorCallback(err);
        }
    }];
}

static dispatch_queue_t XSFileIOQueue() {
    static dispatch_queue_t q;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        q = dispatch_queue_create("com.xs.fileio.queue", DISPATCH_QUEUE_SERIAL);
    });
    return q;
}

static id XSFilterJSONValue(id obj) {
    if (obj == [NSNull null]) return nil;
    if ([obj isKindOfClass:[NSDictionary class]]) {
        NSMutableDictionary *md = [NSMutableDictionary dictionary];
        [(NSDictionary *)obj enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
            if (![key isKindOfClass:[NSString class]]) return;
            id v = XSFilterJSONValue(value);
            if (v) md[key] = v;
        }];
        return md;
    }
    if ([obj isKindOfClass:[NSArray class]]) {
        NSMutableArray *ma = [NSMutableArray array];
        for (id v in (NSArray *)obj) {
            id nv = XSFilterJSONValue(v);
            [ma addObject:nv ?: [NSNull null]];
        }
        return ma;
    }
    // 允许的基础类型
    if ([obj isKindOfClass:[NSString class]] ||
        [obj isKindOfClass:[NSNumber class]]) {
        return obj;
    }
    // 其他类型转字符串以避免序列化失败
    return [obj description];
}

BOOL saveChangeDataFile(NSDictionary *data) {
    if (!data || ![data isKindOfClass:[NSDictionary class]]) {
        NSLog(@"[saveChangeDataFile] Invalid data input: %@", data);
        return NO;
    }

    __block BOOL result = NO;
    __block NSError *writeError = nil;

    dispatch_sync(XSFileIOQueue(), ^{
        @try {
            NSString *jsonPath = @"/var/mobile/Documents/changeapp.json";
            NSString *dirPath = [jsonPath stringByDeletingLastPathComponent];

            NSError *dirErr = nil;
            if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath]) {
                [[NSFileManager defaultManager] createDirectoryAtPath:dirPath
                                          withIntermediateDirectories:YES
                                                           attributes:@{NSFileProtectionKey: NSFileProtectionNone}
                                                                error:&dirErr];
                if (dirErr) {
                    NSLog(@"[saveChangeDataFile] Failed to create directory %@, error: %@", dirPath, dirErr);
                    return;
                }
            }

            NSDictionary *filtered = XSFilterJSONValue(data);
            if (!filtered || ![NSJSONSerialization isValidJSONObject:filtered]) {
                NSLog(@"[saveChangeDataFile] JSON object invalid after filtering: %@", filtered);
                return;
            }

            NSError *jsonError = nil;
            NSData *jsonData = [NSJSONSerialization dataWithJSONObject:filtered
                                                               options:0
                                                                 error:&jsonError];
            if (jsonError || !jsonData) {
                NSLog(@"[saveChangeDataFile] Error serializing to JSON: %@", jsonError);
                return;
            }

            NSURL *fileURL = [NSURL fileURLWithPath:jsonPath];
            result = [jsonData writeToURL:fileURL
                                   options:NSDataWritingAtomic
                                     error:&writeError];

            NSString *idfa = data[@"idfa"];
            NSLog(@"XS- save change file: %@; idfa:%@; path:%@",
                  @(result), idfa, jsonPath);
                  if (!result) {
                    struct rlimit limit;
                    getrlimit(RLIMIT_NOFILE, &limit);
                    NSLog(@"soft limit: %llu, hard limit: %llu", limit.rlim_cur, limit.rlim_max);
                  }
        } @catch (NSException *exception) {
            NSLog(@"[saveChangeDataFile] Exception: %@", exception);
            result = NO;
        }
    });

    if (writeError) {
        NSLog(@"[saveChangeDataFile] Write error: %@", writeError);
    }
    return result;
}

AdLoadInfo getAdLoadInfo(NSDictionary *request) {
    AdLoadInfo info = {0}; // 初始化所有字段为0
    
    if (!request || ![request isKindOfClass:[NSDictionary class]]) {
        NSLog(@"Invalid request");
        return info;
    }
    
    @try {
        NSString *apiUrl = [[XSPhoneConfig sharedInstance] GetFullServerURL:@"ios/top_selection/ad_can_show"];
        if (!apiUrl.length) {
            NSLog(@"Invalid API URL");
            return info;
        }
        
        XSHttpHelper *http = [[XSHttpHelper alloc] init];
        NSData *data = [http doPOST:apiUrl json:[XSHelper dic2Json:request]];
        
        if (!data) {
            NSLog(@"No response data");
            return info;
        }
        
        NSError *jsonError;
        NSDictionary *res = [NSJSONSerialization JSONObjectWithData:data
                                                          options:0
                                                            error:&jsonError];
        
        if (jsonError || ![res isKindOfClass:[NSDictionary class]]) {
            NSLog(@"JSON parsing error: %@", jsonError);
            return info;
        }
        
        NSDictionary *resData = res[@"data"];
        if (![resData isKindOfClass:[NSDictionary class]]) {
            NSLog(@"Invalid response data format");
            return info;
        }
        
        // 安全地获取值
        info.loads = [resData[@"loads"] isKindOfClass:[NSNumber class]] ? resData[@"loads"] : @0;
        info.adTime = [resData[@"adTime"] isKindOfClass:[NSNumber class]] ? resData[@"adTime"] : @0;
        info.adTimeout = [resData[@"adTimeout"] isKindOfClass:[NSNumber class]] ? resData[@"adTimeout"] : @0;
        info.adTouchAfterMs = [resData[@"adTouchAfterMs"] isKindOfClass:[NSNumber class]] ? resData[@"adTouchAfterMs"] : @0;
        info.adTouchBeforeMs = [resData[@"adTouchBeforeMs"] isKindOfClass:[NSNumber class]] ? resData[@"adTouchBeforeMs"] : @0;
        info.touchRate = [resData[@"touchRate"] isKindOfClass:[NSNumber class]] ? resData[@"touchRate"] : @0;
        
    } @catch (NSException *exception) {
        NSLog(@"Error getting ad load info: %@", exception);
    }
    
    return info;
}

void pushInfo(int type, NSString *data, rt_str_callback callback,error_callback errorCallback) {
    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *url = [config GetMainServerURL:@"ios/top_selection/_putinfo"];
    NSString *aesData = [data aesEncrypt:AES_KEY iv:AES_IV];
    NSDictionary *dic = @{
        @"data": aesData,
        @"type": @(type)
    };
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    [http doPOST:url json:[XSHelper dic2Json:dic] withCallback:^(NSData *data) {
        NSDictionary *dic = [XSHelper jsonData2Dictionary:data];
        if (dic && dic[@"data"]) {
            NSString *strData = dic[@"data"];
            NSLog(@"XS- aes data:%@", strData);
            NSString *json = @"";
            if(strData && ![strData isEqual:[NSNull null]]) {
                json = [strData aesDecrypt:AES_KEY iv:AES_IV];
            }
            callback(json);
        }
    } withError:errorCallback];
}

void pushIphoneLog(NSString *data) {
    /*
     logs.ipAddr = log.ipAddr;
             logs.created = log.created;
             logs.data = log.decryptData();
             logs.type = log.type;
             logs.pkgName = log.pkgName;
             logs.version = log.version;
     */
    NSString *aesData = [data aesEncrypt:AES_KEY iv:AES_IV];
    NSString *ipAddr = [[XSPhoneInfo sharedInstance] IPAddress];
    NSString *pkgName =  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"];
    NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
    NSDictionary *dic = @{
        @"ipAddr": ipAddr,
        @"data": aesData,
        @"type": @(2),
        @"pkgName": pkgName,
        @"version": version
    };

    XSPhoneConfig *config = [XSPhoneConfig sharedInstance];
    NSString *url = [config GetMainServerURL:@"ios/top_selection/save_iphone_logs"];
    XSHttpHelper *http = [[XSHttpHelper alloc] init];
    [http doPOST:url json:[XSHelper dic2Json:dic] withCallback:^(NSData *data) {
        NSString *log = [XSHelper data2str:data];
        NSLog(@"pushIphoneLog %@", log);
    } withError:^(NSError *err) {
        NSLog(@"pushIphoneLog %@", err);
    }];
}

void logMessage(NSString *message) {
    NSString *documentsDirectory = @"/var/logs/";
    NSString *logFilePath = [documentsDirectory stringByAppendingPathComponent:@"app_log.txt"];
    
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    NSString *timestamp = [dateFormatter stringFromDate:[NSDate date]];
    
    NSString *logEntry = [NSString stringWithFormat:@"%@: %@\n", timestamp, message];
    
    if (![[NSFileManager defaultManager] fileExistsAtPath:logFilePath]) {
        [logEntry writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
    } else {
        NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
        [fileHandle seekToEndOfFile];
        [fileHandle writeData:[logEntry dataUsingEncoding:NSUTF8StringEncoding]];
        [fileHandle closeFile];
    }
}

void uncaughtExceptionHandler(NSException *exception) {
    NSArray *callStack = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    
    NSString *urlString = [NSString stringWithFormat:@"crash://%@", name];
    NSString *userInfo = [NSString stringWithFormat:@"%@\nreason:\n%@\ncallStackSymbols:\n%@",
                          urlString, reason, [callStack componentsJoinedByString:@"\n"]];
    
    NSLog(@"Crash: %@", userInfo);
    logMessage(userInfo);
    // 这里你可以将崩溃信息保存到文件或发送到服务器
    pushIphoneLog(userInfo);
}

void signalHandler(int signal) {
    /*
    NSMutableString *crashLog = [NSMutableString string];
    [crashLog appendFormat:@"Signal %d was raised.\n", signal];
    
    void* callstack[128];
    int frames = backtrace(callstack, 128);
    char **strs = backtrace_symbols(callstack, frames);
    
    for (int i = 0; i < frames; ++i) {
        [crashLog appendFormat:@"%s\n", strs[i]];
    }
    
    free(strs);
    
    NSLog(@"SpringBoard crash:%@", crashLog);
    */
    const char *signalName = strsignal(signal);
    write(STDERR_FILENO, "Signal received: ", 17);
    write(STDERR_FILENO, signalName, strlen(signalName));
    write(STDERR_FILENO, "\n", 1);
    // 设置标志位，通知主线程处理
    // 避免在信号处理函数中调用复杂逻辑
     _exit(signal);
    
    // 这里你可以将崩溃信息保存到文件或发送到服务器

    // pushIphoneLog(crashLog);
}

void registerSignalHandler(void) {
    signal(SIGABRT, signalHandler);
    signal(SIGILL, signalHandler);
    signal(SIGSEGV, signalHandler);
    signal(SIGFPE, signalHandler);
    signal(SIGBUS, signalHandler);
    signal(SIGPIPE, signalHandler);
    NSSetUncaughtExceptionHandler(&uncaughtExceptionHandler);
}
