// OfflineSyncManager.mm
#import "OfflineSyncManager.h"
#import "LocalStorage.h"
#import "NetworkManager.h"
#import "APIClient.h"

static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";

static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";

static NSString * const kSyncHistoryKey = @"SYNC_HISTORY_QUEUE";

static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";

@implementation OfflineSyncManager

#pragma mark - Singleton

+ (instancetype)shared {
    static OfflineSyncManager *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[OfflineSyncManager alloc] init];
    });
    return instance;
}

#pragma mark - Queue Helpers (CRITICAL)
- (NSMutableArray *)safeLoadQueue {
    id stored = [LocalStorage loadArrayForKey:kOfflineQueueKey];

    NSLog(@"📦 [OfflineSync] Raw stored queue: %@", stored);

    if (![stored isKindOfClass:[NSArray class]]) {
        NSLog(@"🧹 [OfflineSync] Queue corrupted — resetting");
        [LocalStorage removeObjectForKey:kOfflineQueueKey];
        return [NSMutableArray array];
    }

    NSMutableArray *validItems = [NSMutableArray array];
    for (id obj in stored) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [validItems addObject:obj];
        }
    }

    NSLog(@"📦 [OfflineSync] Valid queue items: %@", validItems);

    return validItems;
}

- (void)saveQueue:(NSArray *)queue {
    [LocalStorage saveArray:queue forKey:kOfflineQueueKey];
}

#pragma mark - Public Queue APIs

- (void)queueAddIssue:(NSDictionary *)payload
              display:(NSDictionary *)display
               images:(NSArray<UIImage *> *)images
           fieldNames:(NSDictionary *)fieldNames {

    [self enqueueAction:@"ADD_ISSUE"
                payload:payload
                 images:images
            displayInfo:display
             fieldNames:fieldNames];
}

- (void)queueUpdateIssue:(NSDictionary *)payload
                 display:(NSDictionary *)display
                  images:(NSArray<UIImage *> *)images
              fieldNames:(NSDictionary *)fieldNames {

    [self enqueueAction:@"UPDATE_ISSUE"
                payload:payload
                 images:images
            displayInfo:display
             fieldNames:fieldNames];
}

- (void)queueAddInfo:(NSString *)issueID
             remarks:(NSString *)remarks
         displayInfo:(NSDictionary *)displayInfo
              images:(NSArray<UIImage *> *)images
            mediaURL:(NSURL * _Nullable)mediaURL
           mediaType:(NSString * _Nullable)mediaType {

    NSMutableArray *queue = [self safeLoadQueue];

    NSArray *imagePaths = [self saveImagesToDisk:images];
    NSString *mediaPath = [self saveMediaToDisk:mediaURL];

    NSMutableDictionary *item = [NSMutableDictionary dictionary];
    item[@"id"] = NSUUID.UUID.UUIDString;
    item[@"type"] = @"ADD_INFO";

    // 🔥 API payload
    item[@"issue_id"] = issueID ?: @"";
    item[@"remarks"] = remarks ?: @"";

    // 🔥 Files
    item[@"imagePaths"] = imagePaths ?: @[];
    item[@"timestamp"] = @((NSInteger)[NSDate date].timeIntervalSince1970);

    if (mediaPath) item[@"mediaPath"] = mediaPath;
    if (mediaType) item[@"mediaType"] = mediaType;

    // 🔥 UI display fields (CRITICAL)
    item[@"projectName"] = displayInfo[@"projectName"] ?: @"Unknown Project";
    item[@"unitName"] = displayInfo[@"unitName"] ?: @"-";
    item[@"createdBy"] = displayInfo[@"createdBy"] ?: @"-";
    item[@"issueDisplay"] = displayInfo[@"issueDisplay"] ?: @"Add Info";
    item[@"createdAt"] = displayInfo[@"createdAt"] ?: [self todayDateString];

    [queue addObject:item];
    [self saveQueue:queue];

    NSLog(@"📥 [OfflineSync] Queued ADD_INFO (%lu)", (unsigned long)queue.count);
}

#pragma mark - Core Queue Add
- (void)enqueueAction:(NSString *)type
              payload:(NSDictionary *)payload
               images:(NSArray<UIImage *> *)images
          displayInfo:(NSDictionary *)displayInfo
           fieldNames:(NSDictionary *)fieldNames
{
    NSMutableArray *queue = [self safeLoadQueue];
    NSArray *imagePaths = [self saveImagesToDisk:images];

    NSMutableDictionary *item = [NSMutableDictionary dictionary];
    item[@"id"] = NSUUID.UUID.UUIDString;
    item[@"type"] = type;
    item[@"payload"] = payload ?: @{};
    item[@"imagePaths"] = imagePaths ?: @[];
    item[@"timestamp"] = @((NSInteger)[NSDate date].timeIntervalSince1970);

    // 🔥 UI/display info
    item[@"projectName"] = displayInfo[@"projectName"] ?: @"Unknown Project";
    item[@"unitName"] = displayInfo[@"unitName"] ?: @"-";
    item[@"createdBy"] = displayInfo[@"createdBy"] ?: @"-";
    item[@"issueDisplay"] = displayInfo[@"issueDisplay"] ?: type;
    item[@"createdAt"] = displayInfo[@"createdAt"] ?: [self todayDateString];

    // 🔥 Add the fieldNames dictionary
    item[@"fieldNames"] = fieldNames ?: @{};

    [queue addObject:item];
    [self saveQueue:queue];

    NSLog(@"📥 [OfflineSync] Queued %@ (%lu)", type, (unsigned long)queue.count);
}

#pragma mark - Processing Queue
- (void)processNextItemWithProgress:(void (^)(NSInteger step,
                                              NSInteger total,
                                              NSString *title))progress
                         completion:(void (^)(BOOL finished))completion {

    NSMutableArray *queue = [self safeLoadQueue];
    NSInteger total = queue.count;

    if (total == 0) {
        if (completion) completion(YES);
        return;
    }

    __block NSInteger step = 0;
    __weak typeof(self) weakSelf = self;

    __block void (^processNext)(void);
    __weak __block void (^weakProcessNext)(void);

    processNext = ^{
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;

        NSMutableArray *currentQueue = [strongSelf safeLoadQueue];
        if (currentQueue.count == 0) {
            if (completion) completion(YES);
            return;
        }

        NSDictionary *item = currentQueue.firstObject;
        step++;

        if (progress) {
            NSString *title = item[@"issueDisplay"] ?: item[@"type"] ?: @"Syncing";
            progress(step, total, title);
        }

        NSString *taskKey = [NSString stringWithFormat:@"process_%@", item[@"id"]];

        [strongSelf safeProcessItem:item taskKey:taskKey completion:^(BOOL success) {

            // Save to history
            NSMutableDictionary *historyItem = [item mutableCopy];
            historyItem[@"syncedAt"] = [strongSelf todayDateString];
            historyItem[@"status"] = success ? @"SUCCESS" : @"FAILED";

            NSMutableArray *history = [strongSelf safeLoadHistory];
            [history insertObject:historyItem atIndex:0];
            [strongSelf saveHistoryQueue:history];

            // Cleanup files
            for (NSString *path in item[@"imagePaths"]) {
                [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
            }
            if (item[@"mediaPath"]) {
                [[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil];
            }

            // Remove processed item from queue
            NSMutableArray *updatedQueue = [strongSelf safeLoadQueue];
            if (updatedQueue.count > 0) {
                [updatedQueue removeObjectAtIndex:0];
                [strongSelf saveQueue:updatedQueue];
            }

            // Move to next item
            dispatch_async(dispatch_get_main_queue(), ^{
                if (weakProcessNext) weakProcessNext();
            });
        }];
    };

    weakProcessNext = processNext;
    processNext();
}

- (void)safeProcessItem:(NSDictionary *)item
               taskKey:(NSString *)taskKey
            completion:(void (^)(BOOL success))completion {

    __weak typeof(self) weakSelf = self;

    __block BOOL finished = NO;

    // Centralize completion + logging
    void (^finalize)(BOOL) = ^(BOOL success) {
        if (finished) return;
        finished = YES;

        // Log status
        NSMutableDictionary *status =
            [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
        status[taskKey] = success ? @"SUCCESS" : @"FAILED";
        [LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];

        if (completion) completion(success);
    };

    // Timeout guard (in case API never returns)
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(60.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{
        if (!finished) {
            NSLog(@"⚠️ [OfflineSync] Timeout for item: %@", item[@"type"]);
            finalize(NO);
        }
    });

    NSString *type = item[@"type"] ?: @"";

    NSArray<UIImage *> *images = [self loadImagesFromPaths:item[@"imagePaths"]];
    NSURL *mediaURL = item[@"mediaPath"] ? [NSURL fileURLWithPath:item[@"mediaPath"]] : nil;

    if ([type isEqualToString:@"ADD_INFO"]) {

        if (images.count == 0 && !mediaURL) {

            [APIClient submitAddInfo:item[@"issue_id"]
                             remarks:item[@"remarks"]
                           completion:^(NSDictionary *res, NSError *err) {
                BOOL success = [weakSelf isAPISuccess:res error:err];
                if (!success) NSLog(@"⚠️ ADD_INFO failed: %@", err ?: res);
                finalize(success);
            }];

        } else {

            [APIClient uploadAddInfo:item[@"issue_id"]
                             remarks:item[@"remarks"]
                              images:images
                            mediaURL:mediaURL
                           mediaType:item[@"mediaType"]
                          completion:^(NSDictionary *res, NSError *err) {
                BOOL success = [weakSelf isAPISuccess:res error:err];
                if (!success) NSLog(@"⚠️ ADD_INFO upload failed: %@", err ?: res);
                finalize(success);
            }];
        }
        return;
    }

    if ([type isEqualToString:@"ADD_ISSUE"]) {

        [APIClient submitAddIssue:item[@"payload"]
                           images:images
                        completion:^(NSDictionary *res, NSError *err) {

            [weakSelf logAddIssueDebugWithItem:item images:images res:res err:err];

            BOOL success = [weakSelf isAPISuccess:res error:err];
            if (!success) NSLog(@"⚠️ ADD_ISSUE failed: %@", err ?: res);
            finalize(success);
        }];
        return;
    }

    if ([type isEqualToString:@"UPDATE_ISSUE"]) {

        [APIClient submitUpdateIssue:item[@"payload"]
                              images:images
                           completion:^(NSDictionary *res, NSError *err) {
            BOOL success = [weakSelf isAPISuccess:res error:err];
            if (!success) NSLog(@"⚠️ UPDATE_ISSUE failed: %@", err ?: res);
            finalize(success);
        }];
        return;
    }

    // Unknown type — still finalize
    NSLog(@"⚠️ [OfflineSync] Unknown type: %@", type);
    finalize(NO);
}

#pragma mark - Disk Handling
- (NSArray *)saveImagesToDisk:(NSArray<UIImage *> *)images {

    NSMutableArray *paths = [NSMutableArray array];
    if (![images isKindOfClass:[NSArray class]]) return paths;

    NSString *dir = [self offlineDirectory];

    for (id obj in images) {
        if (![obj isKindOfClass:[UIImage class]]) continue;

        NSString *path = [dir stringByAppendingPathComponent:
                          [NSString stringWithFormat:@"%@.jpg", NSUUID.UUID.UUIDString]];

        NSData *data = UIImageJPEGRepresentation(obj, 0.8);
        if (data && [data writeToFile:path atomically:YES]) {
            [paths addObject:path];
        }
    }
    return paths;
}

- (NSString *)saveMediaToDisk:(NSURL *)url {

    if (!url) return nil;

    NSString *dir = [self offlineDirectory];
    NSString *path = [dir stringByAppendingPathComponent:
                      [NSString stringWithFormat:@"%@.%@",
                       NSUUID.UUID.UUIDString, url.pathExtension]];

    NSData *data = [NSData dataWithContentsOfURL:url];
    if ([data writeToFile:path atomically:YES]) {
        return path;
    }
    return nil;
}

- (NSArray *)loadImagesFromPaths:(NSArray *)paths {

    NSMutableArray *images = [NSMutableArray array];
    for (NSString *path in paths) {
        UIImage *img = [UIImage imageWithContentsOfFile:path];
        if (img) [images addObject:img];
    }
    return images;
}

- (NSString *)offlineDirectory {

    NSString *dir = NSSearchPathForDirectoriesInDomains(
        NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
    dir = [dir stringByAppendingPathComponent:@"offline_files"];

    if (![[NSFileManager defaultManager] fileExistsAtPath:dir]) {
        [[NSFileManager defaultManager] createDirectoryAtPath:dir
                                  withIntermediateDirectories:YES
                                                   attributes:nil
                                                        error:nil];
    }
    return dir;
}

#pragma mark - Status
- (BOOL)hasPendingItems {
    return ([self safeLoadQueue].count > 0);
}

#pragma mark - Pending Screen Helper
- (NSArray *)pendingItemsForUI {

    NSArray *queue = [self safeLoadQueue];
    NSMutableDictionary *grouped = [NSMutableDictionary dictionary];

    for (NSDictionary *item in queue) {

        NSString *project = item[@"projectName"] ?: @"Unknown Project";
        NSMutableArray *items = grouped[project] ?: [NSMutableArray array];

        NSDictionary *uiItem = @{
            @"unitName": item[@"unitName"] ?: @"-",
            @"createdBy": item[@"createdBy"] ?: @"-",
            @"createdOn": item[@"createdAt"] ?: [self formatTimestamp:item[@"timestamp"]],
            @"type": item[@"issueDisplay"] ?: item[@"type"] ?: @"-",
            @"raw": item
        };

        [items addObject:uiItem];
        grouped[project] = items;
    }

    NSMutableArray *sections = [NSMutableArray array];
    for (NSString *project in grouped) {
        [sections addObject:@{
            @"project": project,
            @"items": grouped[project]
        }];
    }

    return sections;
}

#pragma mark - History Helpers
- (NSMutableArray *)safeLoadHistory {
    id stored = [LocalStorage loadArrayForKey:kSyncHistoryKey];

    if (![stored isKindOfClass:[NSArray class]]) {
        [LocalStorage removeObjectForKey:kSyncHistoryKey];
        return [NSMutableArray array];
    }

    NSMutableArray *valid = [NSMutableArray array];
    for (id obj in stored) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [valid addObject:obj];
        }
    }

    return valid;
}

- (void)saveHistory:(NSArray *)history {
    [LocalStorage saveArray:history forKey:kSyncHistoryKey];
}

- (NSArray *)historyItemsForUI {

    NSArray *history = [self loadHistoryQueue];
    NSMutableArray *sections = [NSMutableArray array];

    for (NSDictionary *item in history) {

        NSDictionary *uiItem = @{
            @"unitName": item[@"unitName"] ?: @"-",
            @"createdBy": item[@"createdBy"] ?: @"-",
            @"createdOn": item[@"createdAt"] ?: @"-",
            @"uploadedOn": item[@"syncedAt"] ?: @"-",
            @"type": item[@"issueDisplay"] ?: @"-",
            @"status": item[@"status"] ?: @"FAILED",
            @"raw": item
        };

        [sections addObject:uiItem];
    }

    return sections;
}

- (NSInteger)historyItemCount {
    return [self safeLoadHistory].count;
}

- (NSMutableArray *)loadHistoryQueue {
    id stored = [LocalStorage loadArrayForKey:kSyncHistoryKey];

    if (![stored isKindOfClass:[NSArray class]]) {
        return [NSMutableArray array];
    }

    NSMutableArray *valid = [NSMutableArray array];
    for (id obj in stored) {
        if ([obj isKindOfClass:[NSDictionary class]]) {
            [valid addObject:obj];
        }
    }
    return valid;
}

- (void)saveHistoryQueue:(NSArray *)queue {
    [LocalStorage saveArray:queue forKey:kSyncHistoryKey];
}

- (void)retryHistoryItem:(NSDictionary *)historyItem {

    NSMutableDictionary *queueItem = [historyItem mutableCopy];
    [queueItem removeObjectForKey:@"status"];
    [queueItem removeObjectForKey:@"syncedAt"];

    NSMutableArray *queue = [self safeLoadQueue];
    [queue addObject:queueItem];
    [self saveQueue:queue];

    NSMutableArray *history = [self loadHistoryQueue];
    [history removeObject:historyItem];
    [self saveHistoryQueue:history];
}

- (NSString *)formatTimestamp:(NSNumber *)ts {
    NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts.doubleValue];
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"yyyy-MM-dd";
    return [fmt stringFromDate:date];
}

- (NSString *)todayDateString {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
    formatter.dateFormat = @"yyyy-MM-dd";
    return [formatter stringFromDate:[NSDate date]];
}

- (void)clearOfflineQueue {
    [LocalStorage removeObjectForKey:kOfflineQueueKey];
    [LocalStorage removeObjectForKey:kSyncHistoryKey];
    NSLog(@"🧹 [OfflineSync] Offline queue cleared");
}

#pragma mark - Download Units
- (void)startDownloadUnitImagesWithProgress:(void (^)(NSInteger completed, NSInteger total))progress
                                  completion:(void (^)(BOOL success))completion {

    if (![NetworkManager.sharedManager isConnected]) {
        if (completion) completion(NO);
        return;
    }

    NSString *projectId = @"8";
    NSString *drawingPlanId = @"135";
    NSArray *selectedUnits = [LocalStorage loadArrayForKey:kSelectedOfflineUnitsKey] ?: @[];

    NSMutableArray *tasks = [NSMutableArray array];

    // ------------------------------
    // Task 1 — requestIssues
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task1" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
            NSString *key = [NSString stringWithFormat:@"ISSUES_%@", drawingPlanId];

            [APIClient requestIssues:^(NSDictionary *res, NSError *err) {
                if (err) {
                    NSLog(@"⚠️ Task 1 failed: %@", err);
                    logSuccess(NO);
                } else {
                    NSLog(@"✅ Task 1 succeeded");
                    if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
                    logSuccess(YES);
                }
                safeNext();
            }];

        } next:next];
    }];

    // ------------------------------
    // Task 2 — requestDashboardInfo
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task2" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
            NSString *key = [NSString stringWithFormat:@"DASHBOARD_INFO_%@", drawingPlanId];

            [APIClient requestDashboardInfo:^(NSDictionary *res, NSError *err) {
                if (err) {
                    NSLog(@"⚠️ Task 2 failed: %@", err);
                    logSuccess(NO);
                } else {
                    NSLog(@"✅ Task 2 succeeded");
                    if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
                    logSuccess(YES);
                }
                safeNext();
            }];

        } next:next];
    }];

    // ------------------------------
    // Task 3 — requestGetUnitPlan
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task3" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
            NSString *key = [NSString stringWithFormat:@"PLAN_UNIT_%@", drawingPlanId];

            [APIClient requestGetUnitPlan:^(NSDictionary *res, NSError *err) {
                if (err || ![res isKindOfClass:[NSDictionary class]]) {
                    NSLog(@"⚠️ Task 3 failed: %@", err);
                    logSuccess(NO);
                } else {
                    id dataArray = res[@"Data"];
                    NSDictionary *objData = @{};
                    if ([dataArray isKindOfClass:[NSArray class]] && [dataArray count] > 0) {
                        id firstItem = dataArray[0];
                        if ([firstItem isKindOfClass:[NSDictionary class]]) objData = firstItem;
                    }
                    [LocalStorage saveDictionary:objData forKey:key];
                    logSuccess(YES);
                }
                safeNext();
            }];
        } next:next];
    }];

    // ------------------------------
    // Task 4 — requestGeneralSettings
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task4" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
            NSString *key = [NSString stringWithFormat:@"SETTINGS_%@", projectId];

            [APIClient requestGeneralSettings:^(NSDictionary *res, NSError *err) {
                if (err) {
                    NSLog(@"⚠️ Task 4 failed: %@", err);
                    logSuccess(NO);
                } else {
                    if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
                    logSuccess(YES);
                    NSLog(@"✅ Task 4 succeeded");
                }
                safeNext();
            }];
        } next:next];
    }];

    // ------------------------------
    // Task 5 — requestCommonArea
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task5" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
            NSString *key = [NSString stringWithFormat:@"COMMONAREA_%@", projectId];

            [APIClient requestCommonArea:^(NSDictionary *res, NSError *err) {
                if (err) {
                    NSLog(@"⚠️ Task 5 failed: %@", err);
                    logSuccess(NO);
                } else {
                    if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
                    logSuccess(YES);
                    NSLog(@"✅ Task 5 succeeded");
                }
                safeNext();
            }];
        } next:next];
    }];

    // ------------------------------
    // Task 6 — requestDefectMatrix
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task6" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {

            [APIClient requestDefectMatrix:^(NSDictionary *res, NSError *err) {
                if (!res || err) {
                    NSLog(@"⚠️ Task 6 failed: %@", err);
                    logSuccess(NO);
                    safeNext();
                    return;
                }

                NSDictionary *data = res[@"Data"];
                if (![data isKindOfClass:[NSDictionary class]]) {
                    logSuccess(NO);
                    safeNext();
                    return;
                }

                NSArray *priority        = data[@"priority"] ?: @[];
                NSArray *contractor      = data[@"contractor"] ?: @[];
                NSArray *newIssueStatus  = data[@"new_issue_status"] ?: @[];
                NSArray *startWorkStatus = data[@"start_work_status"] ?: @[];
                NSArray *subContractor   = data[@"sub_contractor"] ?: @[];
                NSArray *locationStatus  = data[@"location_status"] ?: @[];
                NSString *notMe          = data[@"not_me"] ?: @"";

                NSArray *defectMatrix = data[@"defect_matrix"];
                if ([defectMatrix isKindOfClass:[NSArray class]]) {
                    for (NSDictionary *entry in defectMatrix) {
                        NSArray *locationIds = entry[@"location_id"];
                        if (![locationIds isKindOfClass:[NSArray class]] || locationIds.count == 0) continue;

                        NSArray *category = entry[@"category"] ?: @[];
                        NSDictionary *settings = @{
                            @"category": category,
                            @"priority": priority,
                            @"contractor": contractor,
                            @"new_issue_status": newIssueStatus,
                            @"start_work_status": startWorkStatus,
                            @"sub_contractor": subContractor,
                            @"location_status": locationStatus,
                            @"not_me": notMe
                        };

                        for (id locationId in locationIds) {
                            NSString *locationKey = [NSString stringWithFormat:@"LOCATION_SETTINGS_%@", locationId];
                            [LocalStorage saveDictionary:settings forKey:locationKey];
                        }
                    }
                }
                logSuccess(YES);
                safeNext();
            }];
        } next:next];
    }];

    // ------------------------------
    // Task 7 — Download Images
    // ------------------------------
    [tasks addObject:^(void (^next)(void)) {
        [self safeSequentialTask:@"task7" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {

            if (selectedUnits.count == 0) {
                logSuccess(YES);
                safeNext();
                return;
            }

            [self fetchOfflineImageListForSelectedUnits:selectedUnits completion:^(NSArray<NSURL *> *urls, NSError *error) {
                if (error || urls.count == 0) {
                    logSuccess(NO);
                    safeNext();
                    return;
                }

                [self downloadImagesSequentially:urls
                                         progress:^(NSInteger completedImages, NSInteger totalImages) {
                    NSInteger overallCompleted = 6 + completedImages;
                    NSInteger overallTotal = 6 + urls.count;
                    if (progress) progress(overallCompleted, overallTotal);
                } next:^{
                    logSuccess(YES);
                    safeNext();
                }];
            }];
        } next:next];
    }];

    // ------------------------------
    // Sequential executor
    // ------------------------------
    __block NSInteger currentTaskIndex = 0;

    __block void (^runNextTask)(void) = nil;
    __weak typeof(runNextTask) weakRunNextTask = nil;

    runNextTask = ^{
        __strong typeof(weakRunNextTask) strongRunNextTask = weakRunNextTask;
        if (!strongRunNextTask) return;

        if (currentTaskIndex >= tasks.count) {
            [self saveLastOfflineSyncDate];
            if (completion) completion(YES);
            return;
        }

        void (^task)(void (^)(void)) = tasks[currentTaskIndex];
        currentTaskIndex++;

        if (progress && currentTaskIndex <= 6) {
            NSInteger overallTotal = 6 + selectedUnits.count;
            progress(currentTaskIndex, overallTotal);
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            task(strongRunNextTask);
        });
    };

    weakRunNextTask = runNextTask;
    runNextTask();
}

#pragma mark - Robust Image Download
- (void)downloadImagesSequentially:(NSArray<NSURL *> *)urls
                           progress:(void (^)(NSInteger completed, NSInteger total))progress
                               next:(void (^)(void))next {

    if (urls.count == 0) {
        if (next) next();
        return;
    }

    __block NSInteger index = 0;
    NSInteger total = urls.count;

    __block void (^downloadNext)(void) = nil;
    __weak __block void (^weakDownloadNext)(void) = nil;

    downloadNext = ^{
        __strong typeof(weakDownloadNext) strongDownloadNext = weakDownloadNext;
        if (!strongDownloadNext) return;

        if (index >= total) {
            if (next) next();
            return;
        }

        NSURL *url = urls[index];
        NSString *urlKey = [NSString stringWithFormat:@"image_%ld", (long)index];
        index++;

        // Timeout guard
        __block BOOL finished = NO;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            if (!finished) {
                NSLog(@"⚠️ Image download timeout: %@", url);
                NSMutableDictionary *status = [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
                status[urlKey] = @"FAILED";
                [LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
                finished = YES;
                if (progress) progress(index, total);
                strongDownloadNext();
            }
        });

        [APIClient downloadAndCacheImage:url completion:^(BOOL success) {
            if (finished) return;

            NSMutableDictionary *status = [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
            status[urlKey] = success ? @"SUCCESS" : @"FAILED";
            [LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
            finished = YES;

            if (progress) progress(index, total);
            strongDownloadNext();
        }];
    };

    weakDownloadNext = downloadNext;
    downloadNext();
}

#pragma mark - Image Download Helpers
- (NSArray *)buildImageSyncPayloadFromUnits:(NSArray *)units {
    NSMutableDictionary<NSString *, NSMutableArray *> *grouped = @{}.mutableCopy;

    for (NSDictionary *unit in units) {
        NSString *projectId = unit[@"project_id"];
        NSString *planId = unit[@"plan_id"];
        if (!projectId || !planId) continue;

        if (!grouped[projectId]) grouped[projectId] = [NSMutableArray array];
        [grouped[projectId] addObject:planId];
    }

    NSMutableArray *result = [NSMutableArray array];
    for (NSString *projectId in grouped) {
        [result addObject:@{ @"project_id": projectId, @"plan_id": grouped[projectId] }];
    }

    return result;
}

- (void)fetchOfflineImageListForSelectedUnits:(NSArray *)units
                                   completion:(void (^)(NSArray<NSURL *> *, NSError *))completion {

    NSArray *payload = [self buildImageSyncPayloadFromUnits:units];
    if (payload.count == 0) {
        completion(@[], nil);
        return;
    }

    [APIClient requestArrayImage:payload completion:^(NSDictionary *res, NSError *err) {
        if (err) {
            completion(nil, err);
            return;
        }

        NSMutableOrderedSet<NSURL *> *urls = [NSMutableOrderedSet orderedSet];
        NSDictionary *fileImage = res[@"Data"][@"file_image"];

        for (NSDictionary *img in fileImage[@"plan_image"]) {
            if (img[@"link"]) [urls addObject:[NSURL URLWithString:img[@"link"]]];
        }
        for (NSDictionary *img in fileImage[@"issue_image"]) {
            if (img[@"link"]) [urls addObject:[NSURL URLWithString:img[@"link"]]];
        }

        completion(urls.array, nil);
    }];
}

#pragma mark - Safe Sequential API Helper

- (void)safeSequentialTask:(NSString *)taskKey
                     block:(void (^)(void (^next)(void), void (^success)(BOOL)))block
                     next:(void (^)(void))next {

    __weak typeof(self) weakSelf = self;

    // Safe next ensures we never get stuck
    void (^safeNext)(void) = ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            if (next) next();
        });
    };

    // Success logger
    void (^logSuccess)(BOOL) = ^(BOOL success){
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) return;

        NSMutableDictionary *status =
            [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy];
        if (!status) status = [NSMutableDictionary dictionary];

        status[taskKey] = success ? @"SUCCESS" : @"FAILED";
        [LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
    };

    // Execute the block with safeNext and success callback
    block(safeNext, logSuccess);
}

- (void)saveLastOfflineSyncDate {
    NSDate *now = [NSDate date];
    NSTimeInterval timestamp = [now timeIntervalSince1970];

    [LocalStorage saveObject:@(timestamp)
                      forKey:kLastOfflineSyncDateKey];
}

#pragma mark - Pending Items Helpers

- (BOOL)isAPISuccess:(NSDictionary *)res error:(NSError *)err {
    if (err || ![res isKindOfClass:[NSDictionary class]]) {
        return NO;
    }

    NSDictionary *appData = res[@"AppData"];
    if (![appData isKindOfClass:[NSDictionary class]]) {
        return NO;
    }

    NSString *status = appData[@"status"];
    return [status isKindOfClass:[NSString class]] &&
           [[status lowercaseString] isEqualToString:@"success"];
}

- (void)logAddIssueDebugWithItem:(NSDictionary *)item
                          images:(NSArray<UIImage *> *)images
                             res:(NSDictionary *)res
                            err:(NSError *)err {

    NSLog(@"\n🚀 [ADD_ISSUE] ================================");
    NSLog(@"📤 Payload: %@", item[@"payload"]);

    NSLog(@"📸 Images count: %lu", (unsigned long)images.count);

    NSMutableArray *imageSizes = [NSMutableArray array];
    for (UIImage *img in images) {
        CGSize size = img.size;
        [imageSizes addObject:
         [NSString stringWithFormat:@"%.0fx%.0f", size.width, size.height]];
    }
    NSLog(@"📸 Image sizes: %@", imageSizes);

    if (err) {
        NSLog(@"❌ API Error: %@", err);
    } else {
        NSLog(@"📥 API Response: %@", res);
    }

    NSDictionary *appData = res[@"AppData"];
    NSLog(@"📥 AppData: %@", appData);

    NSLog(@"🏁 Status evaluated as: %@",
          [self isAPISuccess:res error:err] ? @"SUCCESS" : @"FAILED");

    NSLog(@"============================================\n");
}


@end
