// DashboardViewController.mm
#import "DashboardViewController.h"
#import <CoreGraphics/CoreGraphics.h>
#import <objc/runtime.h>
#import "PlanViewController.h"
#import "APIClient.h"
#import "IssueDetailViewController.h"
#import "IssuesViewController.h"
#import "AppointmentViewController.h"
#import "AppointmentDetailsViewController.h"
#import "AppointmentViewMoreViewController.h"
#import "GeneralInfoViewController.h"
#import "AccessItemViewController.h"
#import "OfflineSyncManager.h"
#import "LocalStorage.h"
#import "APIConfig.h"
#import "ImageCacheHelper.h"

@interface DashboardViewController () <UIScrollViewDelegate>

@property (nonatomic, strong) NSArray *appointments;
@property (nonatomic, strong) NSArray *issues;
@property (nonatomic, assign) BOOL hasPendingAccess;
@property (nonatomic, assign) BOOL hasSignOff;
@property (nonatomic, strong) NSString *strContactlessHandoverStatus;
@property (nonatomic, strong) NSString *strContactlessFormUrl;
@property (nonatomic, strong) NSString *strPendingAccessReminder;
@property (nonatomic, assign) BOOL bln_Block_Handover;
@property (nonatomic, assign) BOOL bln_Block_Inspection;
@property (nonatomic, assign) NSInteger intPOACount;
@property (nonatomic, assign) NSInteger intPendingAccessCount;
@property (nonatomic, assign) BOOL bln_ReminderPA;
@property (nonatomic, assign) BOOL bln_POASignOff;
@property (nonatomic, strong) UIImage *headerBackgroundImage;
@property (nonatomic, strong) NSArray *announcements;
@property (nonatomic, strong) UIView *loadingOverlay;
@property (nonatomic, strong) UIActivityIndicatorView *spinner;
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIImageView *backgroundImageView;
@property (nonatomic, strong) UIImageView *overlayImageView;
@property (nonatomic, strong) UILabel *projectNameLabel;
@property (nonatomic, strong) UILabel *unitNameLabel;
@property (nonatomic, strong) UIButton *infoButton;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UIStackView *contentContainer;
@property (nonatomic, strong) UIView *inspectionBanner;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIView *stickyBar;


@end

@implementation DashboardViewController {
    NSMutableDictionary *_sectionViews;
    CGFloat _currentY;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor colorWithWhite:0.96 alpha:1.0];
    _sectionViews = [NSMutableDictionary dictionary];
    [self setupScrollView];
    [self setupStickyBar];
    [self setupHeader];
    [self setupScrollContent];
    [self setupFooter];

    [self showLoadingOverlay:@"Loading dashboard..."];

    NSLog(@"dashboard.drawerController = %@", self.drawerController);
  
    //[[OfflineSyncManager shared] clearOfflineQueue];
}

- (void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
  
  NSLog(@"Dashboard loaded, refreshing data");
  [self refreshDashboardContent];
  self.navigationController.navigationBarHidden = YES;
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    self.navigationController.navigationBarHidden = YES;
}

#pragma mark - Refresh Logic
- (void)refreshDashboardContent {
    NSLog(@"🔁 Refreshing dashboard content...");

    dispatch_group_t group = dispatch_group_create();
    __block NSDictionary *dashboardData = nil;
    __block NSDictionary *announcementData = nil;

    dispatch_group_enter(group);
    [APIClient requestDashboardInfo:^(BOOL success, NSDictionary *data, NSError *error) {
        if (!error) dashboardData = data;
        dispatch_group_leave(group);
    }];

    dispatch_group_enter(group);
    [APIClient requestAnnouncement:^(BOOL success, NSDictionary *data, NSError *error) {
        if (!error) announcementData = data;
        dispatch_group_leave(group);
    }];

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        if (dashboardData) [self processDashboardResponse:dashboardData];
        if (announcementData) [self processAnnouncementResponse:announcementData];

        // 👇 hide loading only when EVERYTHING is done
        [self hideLoadingOverlay];

        NSLog(@"✅ Dashboard refresh complete");
    });
}

- (void)handlePullToRefresh:(UIRefreshControl *)refreshControl {
    NSLog(@"🔄 Pull-to-refresh triggered");

    [self refreshDashboardContent];

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
                   dispatch_get_main_queue(), ^{
        [refreshControl endRefreshing];
    });
}

#pragma mark - Layout
//- (void)viewDidLayoutSubviews {
//    NSLog(@"Header frame: %@", NSStringFromCGRect(_headerView.frame));
//    [super viewDidLayoutSubviews];
//    UIView *footerView = self.view.subviews.lastObject;
//    CGFloat footerHeight = 80.0;
//    CGFloat bottomInset = self.view.safeAreaInsets.bottom;
//    footerView.frame = CGRectMake(
//        0,
//        self.view.bounds.size.height - footerHeight - bottomInset,
//        self.view.bounds.size.width,
//        footerHeight + bottomInset
//    );
//}

#pragma mark - Header
- (void)setupStickyBar {
    self.stickyBar = [[UIView alloc] init];
    self.stickyBar.translatesAutoresizingMaskIntoConstraints = NO;
    self.stickyBar.backgroundColor = [UIColor colorWithRed:0 green:0.43 blue:0.55 alpha:0]; // start fully transparent
    [self.view addSubview:self.stickyBar];

    [NSLayoutConstraint activateConstraints:@[
        [self.stickyBar.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
        [self.stickyBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        [self.stickyBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [self.stickyBar.heightAnchor constraintEqualToConstant:44]
    ]];

    // Back button
    self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"]
                     forState:UIControlStateNormal];
    self.backButton.tintColor = UIColor.whiteColor;
    [self.backButton addTarget:self action:@selector(onBack)
              forControlEvents:UIControlEventTouchUpInside];
    [self.stickyBar addSubview:self.backButton];

    // Menu button
    UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeCustom];
    menuButton.translatesAutoresizingMaskIntoConstraints = NO;
    [menuButton setImage:[UIImage systemImageNamed:@"line.horizontal.3"]
                forState:UIControlStateNormal];
    menuButton.tintColor = UIColor.whiteColor;
    [menuButton addTarget:self
                   action:@selector(handleMenuButtonTapped)
         forControlEvents:UIControlEventTouchUpInside];
    [self.stickyBar addSubview:menuButton];

    [NSLayoutConstraint activateConstraints:@[
        [self.backButton.leadingAnchor constraintEqualToAnchor:self.stickyBar.leadingAnchor constant:8],
        [self.backButton.centerYAnchor constraintEqualToAnchor:self.stickyBar.centerYAnchor],
        [self.backButton.widthAnchor constraintEqualToConstant:24],
        [self.backButton.heightAnchor constraintEqualToConstant:24],

        [menuButton.trailingAnchor constraintEqualToAnchor:self.stickyBar.trailingAnchor constant:-16],
        [menuButton.centerYAnchor constraintEqualToAnchor:self.stickyBar.centerYAnchor],
        [menuButton.widthAnchor constraintEqualToConstant:26],
        [menuButton.heightAnchor constraintEqualToConstant:26],
    ]];
  
    UIView *statusBarBackground = [[UIView alloc] init];
    statusBarBackground.translatesAutoresizingMaskIntoConstraints = NO;
    statusBarBackground.backgroundColor =
        [UIColor colorWithRed:0 green:0.43 blue:0.55 alpha:1.0];
    [self.view insertSubview:statusBarBackground aboveSubview:self.scrollView];

    [NSLayoutConstraint activateConstraints:@[
        [statusBarBackground.topAnchor constraintEqualToAnchor:self.view.topAnchor],
        [statusBarBackground.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        [statusBarBackground.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [statusBarBackground.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
    ]];
}

- (void)setupHeader {
    CGFloat headerHeight = 255.0;
    CGFloat maxImageHeight = 255.0;

    // ---------- Header Container ----------
    self.headerView = [[UIView alloc] init];
    self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
    self.headerView.backgroundColor = [UIColor colorWithRed:0 green:0.43 blue:0.55 alpha:1.0];
    [self.contentView addSubview:self.headerView];

    [NSLayoutConstraint activateConstraints:@[
        [self.headerView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
        [self.headerView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
        [self.headerView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
        [self.headerView.heightAnchor constraintEqualToConstant:headerHeight]
    ]];

    // ---------- Image Container (fixed height) ----------
    UIView *imageContainer = [[UIView alloc] init];
    imageContainer.translatesAutoresizingMaskIntoConstraints = NO;
    [self.headerView addSubview:imageContainer];

    [NSLayoutConstraint activateConstraints:@[
        [imageContainer.topAnchor constraintEqualToAnchor:self.headerView.topAnchor],
        [imageContainer.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor],
        [imageContainer.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor],
        [imageContainer.heightAnchor constraintEqualToConstant:maxImageHeight]
    ]];

    // ---------- Background Image ----------
    self.backgroundImageView = [[UIImageView alloc] init];
    self.backgroundImageView.translatesAutoresizingMaskIntoConstraints = NO;
    self.backgroundImageView.contentMode = UIViewContentModeScaleAspectFill;
    self.backgroundImageView.clipsToBounds = YES;
    [imageContainer addSubview:self.backgroundImageView];

    [NSLayoutConstraint activateConstraints:@[
        [self.backgroundImageView.topAnchor constraintEqualToAnchor:imageContainer.topAnchor],
        [self.backgroundImageView.leadingAnchor constraintEqualToAnchor:imageContainer.leadingAnchor],
        [self.backgroundImageView.trailingAnchor constraintEqualToAnchor:imageContainer.trailingAnchor],
        [self.backgroundImageView.bottomAnchor constraintEqualToAnchor:imageContainer.bottomAnchor]
    ]];

    // ---------- Overlay ----------
    self.overlayImageView = [[UIImageView alloc] init];
    self.overlayImageView.translatesAutoresizingMaskIntoConstraints = NO;
    self.overlayImageView.contentMode = UIViewContentModeScaleToFill;
    self.overlayImageView.userInteractionEnabled = NO;
//    self.overlayImageView.alpha = 0.5;
    [imageContainer addSubview:self.overlayImageView];


    [NSLayoutConstraint activateConstraints:@[
        [self.overlayImageView.topAnchor constraintEqualToAnchor:imageContainer.topAnchor],
        [self.overlayImageView.leadingAnchor constraintEqualToAnchor:imageContainer.leadingAnchor],
        [self.overlayImageView.trailingAnchor constraintEqualToAnchor:imageContainer.trailingAnchor],
        [self.overlayImageView.bottomAnchor constraintEqualToAnchor:imageContainer.bottomAnchor]
    ]];

    // ---------- Project Name ----------
    self.projectNameLabel = [[UILabel alloc] init];
    self.projectNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    self.projectNameLabel.font = [UIFont boldSystemFontOfSize:18];
    self.projectNameLabel.textColor = UIColor.whiteColor;
    self.projectNameLabel.textAlignment = NSTextAlignmentCenter;
    [imageContainer addSubview:self.projectNameLabel];

    [NSLayoutConstraint activateConstraints:@[
        [self.projectNameLabel.centerXAnchor constraintEqualToAnchor:self.stickyBar.centerXAnchor],
        [self.projectNameLabel.centerYAnchor constraintEqualToAnchor:self.stickyBar.centerYAnchor]
    ]];

    // ---------- Unit Name ----------
    self.unitNameLabel = [[UILabel alloc] init];
    self.unitNameLabel.translatesAutoresizingMaskIntoConstraints = NO;
    self.unitNameLabel.font = [UIFont boldSystemFontOfSize:20];
    self.unitNameLabel.textColor = UIColor.whiteColor;
    [self.headerView addSubview:self.unitNameLabel];

    [NSLayoutConstraint activateConstraints:@[
        [self.unitNameLabel.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:16],
        [self.unitNameLabel.bottomAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:-16]
    ]];

    // ---------- Info Button ----------
    self.infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.infoButton.translatesAutoresizingMaskIntoConstraints = NO;
    [self.infoButton setImage:[UIImage systemImageNamed:@"info.circle"] forState:UIControlStateNormal];
    self.infoButton.tintColor = UIColor.whiteColor;
    [self.infoButton addTarget:self action:@selector(infoButtonTapped) forControlEvents:UIControlEventTouchUpInside];
    [self.headerView addSubview:self.infoButton];

    [NSLayoutConstraint activateConstraints:@[
        [self.infoButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-16],
        [self.infoButton.bottomAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:-18],
        [self.infoButton.widthAnchor constraintEqualToConstant:24],
        [self.infoButton.heightAnchor constraintEqualToConstant:24]
    ]];
  
    // ---------- Dark Overlay ----------
    UIView *darkOverlay = [[UIView alloc] init];
    darkOverlay.translatesAutoresizingMaskIntoConstraints = NO;
    darkOverlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.35];
    darkOverlay.userInteractionEnabled = NO;

    [imageContainer addSubview:darkOverlay];

    [NSLayoutConstraint activateConstraints:@[
        [darkOverlay.topAnchor constraintEqualToAnchor:imageContainer.topAnchor],
        [darkOverlay.leadingAnchor constraintEqualToAnchor:imageContainer.leadingAnchor],
        [darkOverlay.trailingAnchor constraintEqualToAnchor:imageContainer.trailingAnchor],
        [darkOverlay.bottomAnchor constraintEqualToAnchor:imageContainer.bottomAnchor]
    ]];

    // ---------- Load Remote Image & Overlay ----------
    NSBundle *bundle = [NSBundle bundleForClass:[self class]];
    UIImage *overlayImage =
        [UIImage imageNamed:@"img_overlay"
                   inBundle:bundle
      compatibleWithTraitCollection:nil];
    self.overlayImageView.image = overlayImage;
    [imageContainer bringSubviewToFront:self.overlayImageView];
    
    NSLog(@"[Overlay] bundle = %@", bundle);
    NSLog(@"[Overlay] image = %@", overlayImage);
    NSString *remoteHeaderURL = self.projectLogo;
    if (remoteHeaderURL.length > 0) {
        NSURL *url = [NSURL URLWithString:remoteHeaderURL];
        [ImageCacheHelper loadImageWithURL:url completion:^(UIImage *image) {
            self.backgroundImageView.image = image ?: [UIImage imageNamed:@"header_bg"];
            [imageContainer bringSubviewToFront:self.backgroundImageView];
            [imageContainer bringSubviewToFront:darkOverlay];
            [imageContainer bringSubviewToFront:self.overlayImageView];
            [imageContainer bringSubviewToFront:self.projectNameLabel];
        }];
    } else {
        self.backgroundImageView.image = [UIImage imageNamed:@"header_bg"];
        self.overlayImageView.image = [UIImage imageNamed:@"img_overlay"];
    }
}

#pragma mark - Footer
- (void)setupFooter {
    CGFloat footerHeight = 72.0;
    CGFloat bottomInset = self.view.safeAreaInsets.bottom;
    UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(
        0,
        self.view.bounds.size.height - footerHeight,
        self.view.bounds.size.width,
        footerHeight + bottomInset
    )];
    footerView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth;
    footerView.backgroundColor = [UIColor whiteColor];
    footerView.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
    footerView.layer.shadowOpacity = 0.8;
    footerView.layer.shadowOffset = CGSizeMake(0, -1);
    footerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
    footerView.layer.cornerRadius = 16.0;
    footerView.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner; // top-left & top-right
    footerView.clipsToBounds = YES;
    [self.view addSubview:footerView];

    NSArray *titles = @[ @"Make Appt.", @"Add Issue", @"Access Item" ];
    NSArray *icons = @[ @"calendar", @"plus.circle", @"list.bullet" ];
    CGFloat buttonWidth = self.view.bounds.size.width / titles.count;

    for (NSInteger i = 0; i < titles.count; i++) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
        button.frame = CGRectMake(i * buttonWidth, 0, buttonWidth, footerHeight);
        [button setTitle:titles[i] forState:UIControlStateNormal];
        [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        button.titleLabel.font = [UIFont boldSystemFontOfSize:13];
        button.tintColor = [UIColor whiteColor];
        [button setImage:[UIImage systemImageNamed:icons[i]] forState:UIControlStateNormal];

        button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
        button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
        CGFloat spacing = 6.0;
        CGSize imageSize = button.imageView.frame.size;
        CGSize titleSize = button.titleLabel.frame.size;

        button.titleEdgeInsets = UIEdgeInsetsMake(imageSize.height + spacing, -imageSize.width, 0, 0);
        button.imageEdgeInsets = UIEdgeInsetsMake(-titleSize.height - spacing / 2, 0, 0, -titleSize.width);

        button.tag = i;
        [button addTarget:self action:@selector(footerButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
        [footerView addSubview:button];

        if (i < titles.count - 1) {
            UIView *divider = [[UIView alloc] initWithFrame:CGRectMake((i + 1) * buttonWidth - 0.5, 10, 1, footerHeight - 20)];
            divider.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
            [footerView addSubview:divider];
        }
    }
}

- (void)footerButtonTapped:(UIButton *)sender {
    switch (sender.tag) {
        case 0: {
          NSLog(@"Navigating to Appointment");
            AppointmentViewController *appointVC = [[AppointmentViewController alloc] init];
            appointVC.projectCode = self.projectCode;
            appointVC.projectName = self.projectName;
            appointVC.projectId = @"8";
            appointVC.modalPresentationStyle = UIModalPresentationFullScreen;
            [self presentViewController:appointVC animated:YES completion:nil];
            break;
        }
        case 1: {
          NSLog(@"Navigating to Plan");
            PlanViewController *planVC = [[PlanViewController alloc] init];
            planVC.projectCode = self.projectCode;
            planVC.projectName = self.projectName;
            planVC.modalPresentationStyle = UIModalPresentationFullScreen;
            [self presentViewController:planVC animated:YES completion:nil];
            break;
        }
        case 2: {
          NSLog(@"Navigating to Access Item");
            AccessItemViewController *nextVC = [[AccessItemViewController alloc] init];
            nextVC.unitID = self.projectCode;
            nextVC.unitName = self.projectName;
            nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
            [self presentViewController:nextVC animated:YES completion:nil];
        }
        default:
            break;
    }
}

#pragma mark - Scroll Content
- (void)setupScrollView {
    self.scrollView = [[UIScrollView alloc] init];
    self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    self.scrollView.alwaysBounceVertical = YES;
    self.scrollView.delegate = self;
    [self.view addSubview:self.scrollView];

    self.contentView = [[UIView alloc] init];
    self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.scrollView addSubview:self.contentView];

    [NSLayoutConstraint activateConstraints:@[
        [self.scrollView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
        [self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
        [self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
        [self.scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],

        [self.contentView.topAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.topAnchor],
        [self.contentView.leadingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.leadingAnchor],
        [self.contentView.trailingAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.trailingAnchor],
        [self.contentView.bottomAnchor constraintEqualToAnchor:self.scrollView.contentLayoutGuide.bottomAnchor],
        [self.contentView.widthAnchor constraintEqualToAnchor:self.scrollView.frameLayoutGuide.widthAnchor],
    ]];
}

- (void)setupScrollContent {
    UIView *banner = [self buildInspectionChecklistBanner];
    CGFloat topOffset = 16;

    if (banner) {
        banner.translatesAutoresizingMaskIntoConstraints = NO;
        [self.contentView addSubview:banner];

        [NSLayoutConstraint activateConstraints:@[
            [banner.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:topOffset],
            [banner.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
            [banner.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
            [banner.heightAnchor constraintEqualToConstant:90]
        ]];

        topOffset += 90 + 16;
    }

    UIStackView *stack = [[UIStackView alloc] init];
    stack.axis = UILayoutConstraintAxisVertical;
    stack.spacing = 22;
    stack.translatesAutoresizingMaskIntoConstraints = NO;
    stack.layoutMargins = UIEdgeInsetsMake(0, 16, 16, 16);
    stack.layoutMarginsRelativeArrangement = YES;
    [self.contentView addSubview:stack];
    self.contentContainer = stack;

    [NSLayoutConstraint activateConstraints:@[
        [stack.topAnchor constraintEqualToAnchor:(banner ? banner.bottomAnchor : self.headerView.bottomAnchor)
                                        constant:16],
        [stack.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
        [stack.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
        [stack.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor]
    ]];
}

#pragma mark - Inspection Checklist Banner
- (UIView *)buildInspectionChecklistBanner {
    BOOL blnContactlessHandover = NO;  // mock GLOBALS.BLN_CONTACTLESS_HANDOVER == 1
    BOOL blnInspChecklist = NO;        // mock self.bln_InspChecklist
    NSString *handoverStatus = @"Pending";
    NSInteger daysRemaining = 3;

    // Condition check
    if (!(blnContactlessHandover &&
          blnInspChecklist &&
          [handoverStatus isEqualToString:@"Pending"])) {
        return nil;
    }

    UIView *banner = [[UIView alloc] init];
    banner.translatesAutoresizingMaskIntoConstraints = NO;
    banner.backgroundColor = [UIColor colorWithRed:1.0 green:0.39 blue:0.39 alpha:1.0];
    banner.layer.cornerRadius = 20;
    banner.clipsToBounds = YES;

    // Tap gesture
    UITapGestureRecognizer *tap =
        [[UITapGestureRecognizer alloc] initWithTarget:self
                                                action:@selector(handleInspectionBannerTap)];
    [banner addGestureRecognizer:tap];
    self.inspectionBanner = banner;

    // Title
    UILabel *titleLabel = [[UILabel alloc] init];
    titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
    titleLabel.text = @"Submit Unit Inspection Checklist";
    titleLabel.font = [UIFont boldSystemFontOfSize:16];
    titleLabel.textColor = UIColor.whiteColor;
    titleLabel.numberOfLines = 2;
    [banner addSubview:titleLabel];

    // Arrow
    UIImageView *arrow = [[UIImageView alloc]
        initWithImage:[UIImage systemImageNamed:@"chevron.right"]];
    arrow.translatesAutoresizingMaskIntoConstraints = NO;
    arrow.tintColor = UIColor.whiteColor;
    [banner addSubview:arrow];

    UIView *pill = nil;

    if (daysRemaining > 0) {
        pill = [[UIView alloc] init];
        pill.translatesAutoresizingMaskIntoConstraints = NO;
        pill.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
        pill.layer.cornerRadius = 14;
        pill.clipsToBounds = YES;
        [banner addSubview:pill];

        UIImageView *calendarIcon = [[UIImageView alloc]
            initWithImage:[UIImage systemImageNamed:@"calendar"]];
        calendarIcon.translatesAutoresizingMaskIntoConstraints = NO;
        calendarIcon.tintColor = UIColor.whiteColor;
        [pill addSubview:calendarIcon];

        UILabel *daysLabel = [[UILabel alloc] init];
        daysLabel.translatesAutoresizingMaskIntoConstraints = NO;
        daysLabel.text =
            [NSString stringWithFormat:@"%ld days remaining", (long)daysRemaining];
        daysLabel.textColor = UIColor.whiteColor;
        daysLabel.font = [UIFont boldSystemFontOfSize:13];
        [pill addSubview:daysLabel];

        [NSLayoutConstraint activateConstraints:@[
            [calendarIcon.leadingAnchor constraintEqualToAnchor:pill.leadingAnchor constant:8],
            [calendarIcon.centerYAnchor constraintEqualToAnchor:pill.centerYAnchor],
            [calendarIcon.widthAnchor constraintEqualToConstant:20],
            [calendarIcon.heightAnchor constraintEqualToConstant:20],

            [daysLabel.leadingAnchor constraintEqualToAnchor:calendarIcon.trailingAnchor constant:6],
            [daysLabel.trailingAnchor constraintEqualToAnchor:pill.trailingAnchor constant:-10],
            [daysLabel.centerYAnchor constraintEqualToAnchor:pill.centerYAnchor],
        ]];
    }

    // Banner layout
    NSMutableArray *constraints = [@[
        [banner.heightAnchor constraintGreaterThanOrEqualToConstant:90],

        [titleLabel.topAnchor constraintEqualToAnchor:banner.topAnchor constant:16],
        [titleLabel.leadingAnchor constraintEqualToAnchor:banner.leadingAnchor constant:16],
        [titleLabel.trailingAnchor constraintEqualToAnchor:arrow.leadingAnchor constant:-8],

        [arrow.centerYAnchor constraintEqualToAnchor:banner.centerYAnchor],
        [arrow.trailingAnchor constraintEqualToAnchor:banner.trailingAnchor constant:-16],
        [arrow.widthAnchor constraintEqualToConstant:20],
        [arrow.heightAnchor constraintEqualToConstant:24],
    ] mutableCopy];

    if (pill) {
        [constraints addObjectsFromArray:@[
            [pill.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:14],
            [pill.leadingAnchor constraintEqualToAnchor:banner.leadingAnchor constant:16],
            [pill.heightAnchor constraintEqualToConstant:28],
            [pill.bottomAnchor constraintEqualToAnchor:banner.bottomAnchor constant:-16]
        ]];
    } else {
        [constraints addObject:
            [titleLabel.bottomAnchor constraintEqualToAnchor:banner.bottomAnchor constant:-16]];
    }

    [NSLayoutConstraint activateConstraints:constraints];

    return banner;
}

- (void)handleInspectionBannerTap {
    NSLog(@"🟥 Inspection checklist banner tapped");
    [UIView animateWithDuration:0.1 animations:^{
        self.inspectionBanner.alpha = 0.6;
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.1 animations:^{
            self.inspectionBanner.alpha = 1.0;
        }];
    }];
}

#pragma mark - Dashboard Data Processor
- (void)processDashboardResponse:(NSDictionary *)data {
    NSDictionary *appData = data[@"AppData"];
    NSDictionary *mainData = data[@"Data"];

    if (![appData[@"status"] isEqualToString:@"success"] || !mainData) {
        NSLog(@"⚠️ Dashboard status not success: %@", appData[@"message"]);
        return;
    }

    NSArray *appointments = mainData[@"appointment"] ?: @[];
    NSArray *issues = mainData[@"issue_update"] ?: @[];
    NSNumber *paCount = mainData[@"pa_count"];
    NSNumber *poaCount = mainData[@"poa_count"];
    NSString *accessMessage = mainData[@"access_item_message"];
    NSString *handoverStatus = mainData[@"contactlessHandoverTempStatus"];
    NSString *handoverURL = mainData[@"contactlessHandoverTempURL"];

    BOOL hasPendingAccess = (paCount && paCount.intValue > 0);
    BOOL hasSignOff = (poaCount && poaCount.intValue > 0);

    BOOL blockHandover = NO;
    BOOL blockInspection = NO;
    NSInteger rectificationIssueCount = 0;

    for (NSDictionary *appt in appointments) {
        NSString *type = [appt[@"appointment_type"] lowercaseString];
        NSString *status = [appt[@"status"] lowercaseString];

        if ([type isEqualToString:@"handover"] && [status isEqualToString:@"active"]) {
            blockHandover = YES;
        } else if ([type isEqualToString:@"joint inspection"] && [status isEqualToString:@"active"]) {
            blockInspection = YES;
        } else if ([type isEqualToString:@"rectification"] && ![status isEqualToString:@"cancelled"]) {
            NSArray *issuesList = appt[@"issues"];
            if ([issuesList isKindOfClass:[NSArray class]]) {
                rectificationIssueCount += issuesList.count;
            }
        }
    }

    if (paCount.intValue == rectificationIssueCount) {
        hasPendingAccess = NO;
    }

    // Store results
    self.appointments = appointments;
    self.issues = issues;
    self.hasPendingAccess = hasPendingAccess;
    self.hasSignOff = hasSignOff;
    self.intPOACount = poaCount.intValue;
    self.intPendingAccessCount = paCount.intValue;
    self.bln_Block_Handover = blockHandover;
    self.bln_Block_Inspection = blockInspection;
    self.strPendingAccessReminder = accessMessage ?: @"";
    self.strContactlessHandoverStatus = handoverStatus ?: @"";
    self.strContactlessFormUrl = handoverURL ?: @"";

    NSLog(@"✅ Dashboard parsed:");
    NSLog(@"  • Appointments: %lu", (unsigned long)appointments.count);
    NSLog(@"  • Issues: %lu", (unsigned long)issues.count);
    NSLog(@"  • Pending Access: %d", hasPendingAccess);
    NSLog(@"  • POA Count: %ld", (long)self.intPOACount);
    NSLog(@"  • Block Handover: %d", blockHandover);
    NSLog(@"  • Block Inspection: %d", blockInspection);

    // ✅ UI updates must be on main thread
    dispatch_async(dispatch_get_main_queue(), ^{
        self.projectNameLabel.text = self.projectName;
        self.unitNameLabel.text = self.projectCode;

        [self refreshDashboardSectionsWithData:mainData];
    });
}

- (void)processAnnouncementResponse:(NSDictionary *)data {
    NSLog(@"Handling announcement: %@", data);

    NSDictionary *appData = data[@"AppData"];
    if (![appData[@"status"] isEqualToString:@"success"]) {
        NSLog(@"Announcement request failed: %@", appData[@"message"]);
        return;
    }

    // === Combine ===
    NSDictionary *innerData = data[@"Data"] ?: data[@"data"];
    NSArray *client = innerData[@"client_announcement"] ?: @[];
    NSArray *project = innerData[@"project_announcement"] ?: @[];

    NSMutableArray *merged = [NSMutableArray arrayWithArray:client];
    [merged addObjectsFromArray:project];

    // === Sort by date descending ===
    NSArray *sorted = [merged sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *a, NSDictionary *b) {
        NSString *dateA = a[@"date"] ?: @"";
        NSString *dateB = b[@"date"] ?: @"";
        return [dateB compare:dateA];
    }];

    // === Save to property ===
    self.announcements = sorted;

    // === Save to UserDefaults if needed ===
    [[NSUserDefaults standardUserDefaults] setObject:sorted forKey:@"CLIENT_ANNOUNCEMENTS"];
    [[NSUserDefaults standardUserDefaults] synchronize];

    NSLog(@"✅ Stored %lu announcements", (unsigned long)sorted.count);
}

- (void)refreshDashboardSectionsWithData:(NSDictionary *)mainData {
    NSLog(@"🔄 Refreshing dashboard sections...");

    // Remove any old views from scroll
    for (UIView *view in self.contentContainer.arrangedSubviews) {
        [self.contentContainer removeArrangedSubview:view];
        [view removeFromSuperview];
    }

    // === PROJECT UPDATES ===
    NSArray *announcements = self.announcements;
    if (announcements.count > 0) {
        UIView *announcementSection = [self buildAnnouncementSection:announcements];
        if (announcementSection) {
            [self.contentContainer addArrangedSubview:announcementSection];
        }
    }

    // === APPOINTMENTS ===
    NSArray *appointments = mainData[@"appointment"];
    if (appointments.count > 0) {
        UIView *appointmentSection = [self buildAppointmentSection:appointments];
        if (appointmentSection) {
            [self.contentContainer addArrangedSubview:appointmentSection];
        }
    }

    // === ISSUE UPDATES ===
    NSArray *issues = mainData[@"issue_update"];
    if (issues.count > 0) {
        UIView *issueSection = [self buildIssueSection:issues];
        if (issueSection) {
            [self.contentContainer addArrangedSubview:issueSection];
        }
    }

    // Bottom padding so content doesn't kiss footer
    UIView *bottomSpacer = [[UIView alloc] init];
    [bottomSpacer.heightAnchor constraintEqualToConstant:24].active = YES;
    [self.contentContainer addArrangedSubview:bottomSpacer];
}

#pragma mark - Announcement Section
- (UIView *)buildAnnouncementSection:(NSArray *)announcements {
    UIView *section = [[UIView alloc] init];
    section.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentContainer addSubview:section]; // MUST come first

    [NSLayoutConstraint activateConstraints:@[
        [section.leadingAnchor constraintEqualToAnchor:self.contentContainer.leadingAnchor],
        [section.trailingAnchor constraintEqualToAnchor:self.contentContainer.trailingAnchor]
    ]];

    // --- Header ---
    UIButton *headerButton = [UIButton buttonWithType:UIButtonTypeCustom];
    headerButton.translatesAutoresizingMaskIntoConstraints = NO;
    [section addSubview:headerButton];

//    [headerButton addTarget:self
//                     action:@selector(appointmentHeaderTapped:)
//           forControlEvents:UIControlEventTouchUpInside];

    UILabel *title = [[UILabel alloc] init];
    title.translatesAutoresizingMaskIntoConstraints = NO;
    title.text = @"Project Updates";
    title.font = [UIFont boldSystemFontOfSize:18];
    title.textColor = UIColor.blackColor;
    title.userInteractionEnabled = NO; // important
    [headerButton addSubview:title];

    UIImageView *arrow = [[UIImageView alloc] initWithImage:
        [UIImage systemImageNamed:@"chevron.forward"]];
    arrow.translatesAutoresizingMaskIntoConstraints = NO;
    arrow.userInteractionEnabled = NO;
    arrow.tintColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];;
    [headerButton addSubview:arrow];

    [NSLayoutConstraint activateConstraints:@[
        [headerButton.topAnchor constraintEqualToAnchor:section.topAnchor],
        [headerButton.leadingAnchor constraintEqualToAnchor:section.leadingAnchor],
        [headerButton.trailingAnchor constraintEqualToAnchor:section.trailingAnchor],
        [headerButton.heightAnchor constraintEqualToConstant:44],

        [title.centerYAnchor constraintEqualToAnchor:headerButton.centerYAnchor],
        [title.leadingAnchor constraintEqualToAnchor:headerButton.leadingAnchor constant:16],

        [arrow.centerYAnchor constraintEqualToAnchor:title.centerYAnchor],
        [arrow.trailingAnchor constraintEqualToAnchor:title.trailingAnchor constant:20],
        [arrow.widthAnchor constraintEqualToConstant:12],
        [arrow.heightAnchor constraintEqualToConstant:18],
    ]];

    // --- Horizontal Scroll ---
    UIScrollView *hScroll = [[UIScrollView alloc] init];
    hScroll.translatesAutoresizingMaskIntoConstraints = NO;
    hScroll.showsHorizontalScrollIndicator = NO;
    [section addSubview:hScroll];

    [NSLayoutConstraint activateConstraints:@[
        [hScroll.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
        [hScroll.leadingAnchor constraintEqualToAnchor:section.leadingAnchor],
        [hScroll.trailingAnchor constraintEqualToAnchor:section.trailingAnchor],
        [hScroll.heightAnchor constraintEqualToConstant:80],
        [hScroll.bottomAnchor constraintEqualToAnchor:section.bottomAnchor] // important
    ]];

    UIStackView *cards = [[UIStackView alloc] init];
    cards.axis = UILayoutConstraintAxisHorizontal;
    cards.spacing = 12;
    cards.translatesAutoresizingMaskIntoConstraints = NO;
    [hScroll addSubview:cards];

    [NSLayoutConstraint activateConstraints:@[
        [cards.topAnchor constraintEqualToAnchor:hScroll.topAnchor],
        [cards.bottomAnchor constraintEqualToAnchor:hScroll.bottomAnchor],
        [cards.leadingAnchor constraintEqualToAnchor:hScroll.leadingAnchor constant:16],
        [cards.trailingAnchor constraintEqualToAnchor:hScroll.trailingAnchor constant:-16],
        [cards.heightAnchor constraintEqualToAnchor:hScroll.heightAnchor]
    ]];

    NSArray *limited = announcements.count > 5 ? [announcements subarrayWithRange:NSMakeRange(0, 5)] : announcements;
    for (NSDictionary *item in limited) {
        UIView *card = [self makeAnnouncementCard:item];
        card.translatesAutoresizingMaskIntoConstraints = NO;
        [cards addArrangedSubview:card];

      }

    return section;
}

- (UIView *)makeAnnouncementCard:(NSDictionary *)item {
    UIView *card = [[UIView alloc] init];
    card.translatesAutoresizingMaskIntoConstraints = NO;
    card.backgroundColor = UIColor.whiteColor;
    card.layer.cornerRadius = 8;
    card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
    card.layer.shadowOpacity = 0.3;
    card.layer.shadowOffset = CGSizeMake(0, 1);

    // Fixed card size (horizontal scroll needs this)
    [NSLayoutConstraint activateConstraints:@[
        [card.widthAnchor constraintEqualToConstant:272],
        [card.heightAnchor constraintEqualToConstant:80]
    ]];

    // Resolve date
    NSString *dateString = item[@"created_at"] ?: item[@"date"];
    NSString *dateOnly = dateString;
    if ([dateOnly containsString:@" "]) {
        dateOnly = [[dateOnly componentsSeparatedByString:@" "] firstObject];
    }

    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"yyyy-MM-dd";
    NSDate *dateObj = [fmt dateFromString:dateOnly];
    fmt.dateFormat = @"dd/MM/yyyy";

    UILabel *date = [[UILabel alloc] init];
    date.translatesAutoresizingMaskIntoConstraints = NO;
    date.text = dateObj ? [fmt stringFromDate:dateObj] : @"--/--/----";
    date.font = [UIFont systemFontOfSize:13];
    date.textColor = UIColor.grayColor;
    [card addSubview:date];

    UILabel *title = [[UILabel alloc] init];
    title.translatesAutoresizingMaskIntoConstraints = NO;
    title.text = item[@"title"] ?: @"Untitled announcement";
    title.font = [UIFont boldSystemFontOfSize:16];
    title.numberOfLines = 3;
    title.textColor = UIColor.blackColor;
    [card addSubview:title];

    [NSLayoutConstraint activateConstraints:@[
        [date.topAnchor constraintEqualToAnchor:card.topAnchor constant:8],
        [date.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:12],

        [title.topAnchor constraintEqualToAnchor:date.bottomAnchor constant:4],
        [title.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:12],
        [title.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
        [title.bottomAnchor constraintLessThanOrEqualToAnchor:card.bottomAnchor constant:-8]
    ]];

    UITapGestureRecognizer *tap =
        [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleAnnouncementTap:)];
    [card addGestureRecognizer:tap];

    return card;
}

#pragma mark - Appointment Section
- (UIView *)buildAppointmentSection:(NSArray *)appointments {
    UIView *section = [[UIView alloc] init];
    section.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentContainer addSubview:section];

    [NSLayoutConstraint activateConstraints:@[
        [section.leadingAnchor constraintEqualToAnchor:self.contentContainer.leadingAnchor],
        [section.trailingAnchor constraintEqualToAnchor:self.contentContainer.trailingAnchor]
    ]];
    
    // Header
    UIButton *headerButton = [UIButton buttonWithType:UIButtonTypeCustom];
    headerButton.translatesAutoresizingMaskIntoConstraints = NO;
    [section addSubview:headerButton];

    [headerButton addTarget:self
                     action:@selector(appointmentHeaderTapped:)
           forControlEvents:UIControlEventTouchUpInside];

    UILabel *title = [[UILabel alloc] init];
    title.translatesAutoresizingMaskIntoConstraints = NO;
    title.text = @"Appointments";
    title.font = [UIFont boldSystemFontOfSize:18];
    title.textColor = UIColor.blackColor;
    title.userInteractionEnabled = NO; // important
    [headerButton addSubview:title];

    UIImageView *arrow = [[UIImageView alloc] initWithImage:
        [UIImage systemImageNamed:@"chevron.forward"]];
    arrow.translatesAutoresizingMaskIntoConstraints = NO;
    arrow.userInteractionEnabled = NO;
    arrow.tintColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
    [headerButton addSubview:arrow];
  
    [NSLayoutConstraint activateConstraints:@[
        [headerButton.topAnchor constraintEqualToAnchor:section.topAnchor],
        [headerButton.leadingAnchor constraintEqualToAnchor:section.leadingAnchor],
        [headerButton.trailingAnchor constraintEqualToAnchor:section.trailingAnchor],
        [headerButton.heightAnchor constraintEqualToConstant:44],

        [title.centerYAnchor constraintEqualToAnchor:headerButton.centerYAnchor],
        [title.leadingAnchor constraintEqualToAnchor:headerButton.leadingAnchor constant:16],

        [arrow.centerYAnchor constraintEqualToAnchor:title.centerYAnchor],
        [arrow.trailingAnchor constraintEqualToAnchor:title.trailingAnchor constant:25],
        [arrow.widthAnchor constraintEqualToConstant:12],
        [arrow.heightAnchor constraintEqualToConstant:18],
    ]];
  
    // Vertical stack for cards
    UIStackView *list = [[UIStackView alloc] init];
    list.axis = UILayoutConstraintAxisVertical;
    list.spacing = 12;
    list.translatesAutoresizingMaskIntoConstraints = NO;
    list.layoutMargins = UIEdgeInsetsMake(10, 16, 16, 16); // consistent padding
    list.layoutMarginsRelativeArrangement = YES;
    list.alignment = UIStackViewAlignmentFill; // fill width
    [section addSubview:list];

    [NSLayoutConstraint activateConstraints:@[
        [list.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
        [list.leadingAnchor constraintEqualToAnchor:section.leadingAnchor],
        [list.trailingAnchor constraintEqualToAnchor:section.trailingAnchor],
        [list.bottomAnchor constraintEqualToAnchor:section.bottomAnchor]
    ]];

    NSArray *limited = appointments.count > 3 ? [appointments subarrayWithRange:NSMakeRange(0, 3)] : appointments;
    for (NSDictionary *item in limited) {
        UIView *card = [self makeAppointmentCard:item];
        [list addArrangedSubview:card];
    }

    return section;
}


- (UIView *)makeAppointmentCard:(NSDictionary *)item {
    UIView *card = [[UIView alloc] init];
    card.translatesAutoresizingMaskIntoConstraints = NO;
    card.backgroundColor = UIColor.whiteColor;
    card.layer.cornerRadius = 8;
    card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
    card.layer.shadowOpacity = 0.3;
    card.layer.shadowOffset = CGSizeMake(0, 1);

    [card.heightAnchor constraintEqualToConstant:92].active = YES;

    NSString *appointmentType = item[@"appointment_type"] ?: @"-";
    NSString *status = item[@"status"] ?: @"-";
    NSString *startDate = item[@"start_date"] ?: @"-";
    NSString *startTime = item[@"start_time"] ?: @"-";

    NSString *buyerName = @"-";
    NSArray *buyers = item[@"primary_buyer"];
    if ([buyers isKindOfClass:NSArray.class] && buyers.count) {
        buyerName = buyers.firstObject[@"name"] ?: @"-";
    }

    UIView *iconCircle = [[UIView alloc] init];
    iconCircle.translatesAutoresizingMaskIntoConstraints = NO;
    iconCircle.backgroundColor = [UIColor colorWithRed:1.0 green:0.4 blue:0.4 alpha:1.0];
    iconCircle.layer.cornerRadius = 12;
    [card addSubview:iconCircle];

    UIImageView *bell = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"bell.fill"]];
    bell.translatesAutoresizingMaskIntoConstraints = NO;
    bell.tintColor = UIColor.whiteColor;
    [iconCircle addSubview:bell];

    UILabel *statusLabel = [[UILabel alloc] init];
    statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
    statusLabel.text = status;
    statusLabel.font = [UIFont boldSystemFontOfSize:17];
    statusLabel.textColor = [UIColor colorWithRed:0 green:0.43 blue:0.55 alpha:1];
    [card addSubview:statusLabel];

    UILabel *timeLabel = [[UILabel alloc] init];
    timeLabel.translatesAutoresizingMaskIntoConstraints = NO;
    timeLabel.text = startTime;
    timeLabel.textAlignment = NSTextAlignmentRight;
    timeLabel.font = [UIFont boldSystemFontOfSize:15];
    timeLabel.textColor = UIColor.grayColor;
    [card addSubview:timeLabel];

    UILabel *desc = [[UILabel alloc] init];
    desc.translatesAutoresizingMaskIntoConstraints = NO;
    desc.text = [NSString stringWithFormat:
        @"You have a %@ appointment on %@, %@", appointmentType, startDate, buyerName];
    desc.font = [UIFont systemFontOfSize:15];
    desc.numberOfLines = 2;
    [card addSubview:desc];

    [NSLayoutConstraint activateConstraints:@[
        [iconCircle.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
        [iconCircle.topAnchor constraintEqualToAnchor:card.topAnchor constant:12],
        [iconCircle.widthAnchor constraintEqualToConstant:24],
        [iconCircle.heightAnchor constraintEqualToConstant:24],

        [bell.centerXAnchor constraintEqualToAnchor:iconCircle.centerXAnchor],
        [bell.centerYAnchor constraintEqualToAnchor:iconCircle.centerYAnchor],
        [bell.widthAnchor constraintEqualToConstant:16],
        [bell.heightAnchor constraintEqualToConstant:16],

        [statusLabel.leadingAnchor constraintEqualToAnchor:iconCircle.trailingAnchor constant:8],
        [statusLabel.topAnchor constraintEqualToAnchor:card.topAnchor constant:14],

        [timeLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
        [timeLabel.centerYAnchor constraintEqualToAnchor:statusLabel.centerYAnchor],

        [desc.topAnchor constraintEqualToAnchor:card.topAnchor constant:41],
        [desc.leadingAnchor constraintEqualToAnchor:statusLabel.leadingAnchor],
        [desc.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-20],
        [desc.bottomAnchor constraintLessThanOrEqualToAnchor:card.bottomAnchor constant:-15]
    ]];

    objc_setAssociatedObject(card, "appointmentData", item, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    UITapGestureRecognizer *tap =
        [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleAppointmentTap:)];
    [card addGestureRecognizer:tap];

    return card;
}

#pragma mark - Issue Section
- (UIView *)buildIssueSection:(NSArray *)issues {
    UIView *section = [[UIView alloc] init];
    section.translatesAutoresizingMaskIntoConstraints = NO;
    [self.contentContainer addSubview:section];

    [NSLayoutConstraint activateConstraints:@[
        [section.leadingAnchor constraintEqualToAnchor:self.contentContainer.leadingAnchor],
        [section.trailingAnchor constraintEqualToAnchor:self.contentContainer.trailingAnchor]
    ]];

    // Header
    UIButton *headerButton = [UIButton buttonWithType:UIButtonTypeCustom];
    headerButton.translatesAutoresizingMaskIntoConstraints = NO;
    [section addSubview:headerButton];

    [headerButton addTarget:self
                     action:@selector(issueHeaderTapped:)
           forControlEvents:UIControlEventTouchUpInside];

    UILabel *title = [[UILabel alloc] init];
    title.translatesAutoresizingMaskIntoConstraints = NO;
    title.text = @"Issue Updates";
    title.font = [UIFont boldSystemFontOfSize:18];
    title.textColor = UIColor.blackColor;
    title.userInteractionEnabled = NO; // important
    [headerButton addSubview:title];

    UIImageView *arrow = [[UIImageView alloc] initWithImage:
        [UIImage systemImageNamed:@"chevron.forward"]];
    arrow.translatesAutoresizingMaskIntoConstraints = NO;
    arrow.userInteractionEnabled = NO;
    arrow.tintColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
    [headerButton addSubview:arrow];

    [NSLayoutConstraint activateConstraints:@[
        [headerButton.topAnchor constraintEqualToAnchor:section.topAnchor],
        [headerButton.leadingAnchor constraintEqualToAnchor:section.leadingAnchor],
        [headerButton.trailingAnchor constraintEqualToAnchor:section.trailingAnchor],
        [headerButton.heightAnchor constraintEqualToConstant:44],

        [title.centerYAnchor constraintEqualToAnchor:headerButton.centerYAnchor],
        [title.leadingAnchor constraintEqualToAnchor:headerButton.leadingAnchor constant:16],

        [arrow.centerYAnchor constraintEqualToAnchor:title.centerYAnchor],
        [arrow.trailingAnchor constraintEqualToAnchor:title.trailingAnchor constant:20],
        [arrow.widthAnchor constraintEqualToConstant:12],
        [arrow.heightAnchor constraintEqualToConstant:18],
    ]];

    // Vertical stack for cards
    UIStackView *list = [[UIStackView alloc] init];
    list.axis = UILayoutConstraintAxisVertical;
    list.spacing = 12;
    list.translatesAutoresizingMaskIntoConstraints = NO;
    list.layoutMargins = UIEdgeInsetsMake(10, 16, 16, 16); // consistent padding
    list.layoutMarginsRelativeArrangement = YES;
    list.alignment = UIStackViewAlignmentFill; // fill width
    [section addSubview:list];

    [NSLayoutConstraint activateConstraints:@[
        [list.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
        [list.leadingAnchor constraintEqualToAnchor:section.leadingAnchor],
        [list.trailingAnchor constraintEqualToAnchor:section.trailingAnchor],
        [list.bottomAnchor constraintEqualToAnchor:section.bottomAnchor]
    ]];

    for (NSDictionary *item in issues) {
        UIView *card = [self makeIssueCard:item];
        [list addArrangedSubview:card];
    }

    return section;
}

- (UIView *)makeIssueCard:(NSDictionary *)item {
    UIView *card = [[UIView alloc] init];
    card.translatesAutoresizingMaskIntoConstraints = NO;
    card.backgroundColor = UIColor.whiteColor;
    card.layer.cornerRadius = 8;
    card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
    card.layer.shadowOpacity = 0.3;
    card.layer.shadowOffset = CGSizeMake(0, 1);

    [card.heightAnchor constraintGreaterThanOrEqualToConstant:92].active = YES;

    NSString *status = item[@"status_external"] ?: @"Unknown";
    NSString *formattedDate = [self formatDate:item[@"created_at"]];
    UIColor *statusColor = [self colorForStatus:status];

    UIView *dot = [[UIView alloc] init];
    dot.translatesAutoresizingMaskIntoConstraints = NO;
    dot.backgroundColor = statusColor;
    dot.layer.cornerRadius = 8;
    [card addSubview:dot];

    UILabel *statusLabel = [[UILabel alloc] init];
    statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
    statusLabel.text = status;
    statusLabel.font = [UIFont boldSystemFontOfSize:15];
    statusLabel.textColor = statusColor;
    [card addSubview:statusLabel];

    UILabel *dateLabel = [[UILabel alloc] init];
    dateLabel.translatesAutoresizingMaskIntoConstraints = NO;
    dateLabel.text = formattedDate;
    dateLabel.font = [UIFont systemFontOfSize:13];
    dateLabel.textColor = UIColor.grayColor;
    dateLabel.textAlignment = NSTextAlignmentRight;
    [card addSubview:dateLabel];
  
    CGFloat imageSize = 30;
    UIImageView *thumb = [[UIImageView alloc] init];
    thumb.translatesAutoresizingMaskIntoConstraints = NO;
    thumb.layer.cornerRadius = 6;
    thumb.clipsToBounds = YES;
    thumb.contentMode = UIViewContentModeScaleAspectFill;
    thumb.image = [UIImage imageNamed:@"small_placeholder"]; // default

    // Determine issue image
    NSString *issueImage = item[@"image"];
    if (!issueImage || [issueImage isEqualToString:@""]) {
        NSArray *firstArray = item[@"first"];
        NSArray *lastArray = item[@"last"];
        if (firstArray.count > 0 && firstArray[0][@"image"])
            issueImage = firstArray[0][@"image"];
        else if (lastArray.count > 0 && lastArray[0][@"image"])
            issueImage = lastArray[0][@"image"];
    }

    if (issueImage.length > 0) {
        NSURL *url = [NSURL URLWithString:issueImage];
        if (url) {
            [ImageCacheHelper loadImageWithURL:url completion:^(UIImage *image) {
                if (image) thumb.image = image;
            }];
        }
    }

    [card addSubview:thumb];

    UILabel *info = [[UILabel alloc] init];
    info.translatesAutoresizingMaskIntoConstraints = NO;
    info.numberOfLines = 3;
    info.font = [UIFont systemFontOfSize:15];
    info.attributedText = [self notificationTextForIssue:item];
    [card addSubview:info];

    [NSLayoutConstraint activateConstraints:@[
        [dot.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
        [dot.topAnchor constraintEqualToAnchor:card.topAnchor constant:12],
        [dot.widthAnchor constraintEqualToConstant:16],
        [dot.heightAnchor constraintEqualToConstant:16],

        [statusLabel.leadingAnchor constraintEqualToAnchor:dot.trailingAnchor constant:14],
        [statusLabel.centerYAnchor constraintEqualToAnchor:dot.centerYAnchor],

        [dateLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
        [dateLabel.centerYAnchor constraintEqualToAnchor:dot.centerYAnchor],

        [thumb.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:12],
        [thumb.topAnchor constraintEqualToAnchor:dot.bottomAnchor constant:12],
        [thumb.widthAnchor constraintEqualToConstant:imageSize],
        [thumb.heightAnchor constraintEqualToConstant:imageSize],

        [info.leadingAnchor constraintEqualToAnchor:thumb.trailingAnchor constant:14],
        [info.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-14],
        [info.topAnchor constraintEqualToAnchor:card.topAnchor constant:41],
        [info.bottomAnchor constraintLessThanOrEqualToAnchor:card.bottomAnchor constant:-10]
    ]];

    objc_setAssociatedObject(card, "issueData", item, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    UITapGestureRecognizer *tap =
        [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleIssueTap:)];
    [card addGestureRecognizer:tap];

    return card;
}

// Convert hex string (like "#F14915") to UIColor
- (UIColor *)colorFromHex:(NSString *)hexString {
    unsigned rgbValue = 0;
    NSScanner *scanner = [NSScanner scannerWithString:hexString];
    [scanner setScanLocation:1];
    [scanner scanHexInt:&rgbValue];
    return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0
                           green:((rgbValue & 0x00FF00) >> 8) / 255.0
                            blue:(rgbValue & 0x0000FF) / 255.0
                           alpha:1.0];
}

// Status → Color
- (UIColor *)colorForStatus:(NSString *)status {
    if ([status isEqualToString:@"Lodged"]) return [self colorFromHex:@"#2952D6"];
    if ([status isEqualToString:@"New"]) return [self colorFromHex:@"#F99100"];
    if ([status isEqualToString:@"Pending Start"]) return [self colorFromHex:@"#BD5607"];
    if ([status isEqualToString:@"Withdraw"] || [status isEqualToString:@"WITHDRAW"] || [status isEqualToString:@"VOID"])
        return [self colorFromHex:@"#EFC089"];
    if ([status isEqualToString:@"WIP"] || [status isEqualToString:@"W.I.P"])
        return [self colorFromHex:@"#F6C607"];
    if ([status isEqualToString:@"Completed"]) return [self colorFromHex:@"#DF00D0"];
    if ([status isEqualToString:@"Pending Access"]) return [self colorFromHex:@"#F14915"];
    if ([status isEqualToString:@"Pending Owner Acceptance"]) return [self colorFromHex:@"#EF7215"];
    if ([status isEqualToString:@"Rejected"]) return [self colorFromHex:@"#FF3939"];
    if ([status isEqualToString:@"Close Internal"]) return [self colorFromHex:@"#58BA63"];
    if ([status isEqualToString:@"Close External"]) return [self colorFromHex:@"#1C7C00"];
    return [UIColor darkGrayColor];
}

- (NSAttributedString *)notificationTextForIssue:(NSDictionary *)issue {
    NSString *status = issue[@"status_external"] ?: @"";
    NSString *issueRef = issue[@"issue_reference"] ?: @"";
    NSString *projectName = self.projectName; // Replace with GLOBALS.PROJECT_NAME if available

    // Base sentence builder
    NSString *text = @"";
    if ([status isEqualToString:@"New"]) {
        text = [NSString stringWithFormat:@"New issue %@ from %@ has been created.", issueRef, projectName];
    } else if ([status isEqualToString:@"Lodged"]) {
        text = [NSString stringWithFormat:@"%@ from %@ has been lodged.", issueRef, projectName];
    } else if ([status isEqualToString:@"Pending Start"]) {
        text = [NSString stringWithFormat:@"%@ from %@ is pending start.", issueRef, projectName];
    } else if ([status isEqualToString:@"Pending Access"]) {
        text = [NSString stringWithFormat:@"%@ from %@ is pending access.", issueRef, projectName];
    } else if ([status isEqualToString:@"W.I.P"]) {
        text = [NSString stringWithFormat:@"%@ from %@ has changed status to W.I.P.", issueRef, projectName];
    } else if ([status isEqualToString:@"Not Me"]) {
        text = [NSString stringWithFormat:@"%@ from %@ has changed status to Not Me.", issueRef, projectName];
    } else if ([status isEqualToString:@"Reassign"]) {
        text = [NSString stringWithFormat:@"%@ from %@ needs to be reassigned.", issueRef, projectName];
    } else if ([status isEqualToString:@"Completed"]) {
        text = [NSString stringWithFormat:@"%@ from %@ is completed.", issueRef, projectName];
    } else if ([status isEqualToString:@"Redo"]) {
        text = [NSString stringWithFormat:@"%@ from %@ has changed status to Redo.", issueRef, projectName];
    } else if ([status isEqualToString:@"Close Internal"]) {
        text = [NSString stringWithFormat:@"%@ from %@ is closed internally.", issueRef, projectName];
    } else if ([status isEqualToString:@"Close External"]) {
        text = [NSString stringWithFormat:@"%@ from %@ is closed externally.", issueRef, projectName];
    } else if ([status isEqualToString:@"Rejected"] ||
               [status isEqualToString:@"Decline"] ||
               [status isEqualToString:@"Withdraw"]) {
        text = [NSString stringWithFormat:@"%@ from %@ has changed status to %@.", issueRef, projectName, status];
    } else if ([status isEqualToString:@"Pending Owner Acceptance"] ||
               [status isEqualToString:@"POA"] ||
               [status isEqualToString:@"poa"]) {
        text = [NSString stringWithFormat:@"%@ from %@ has changed status to Pending Owner Acceptance.", issueRef, projectName];
    } else {
        text = [NSString stringWithFormat:@"Issue %@ (%@)", issueRef, status];
    }

    // === Create the base attributed string ===
    NSMutableAttributedString *attrText = [[NSMutableAttributedString alloc] initWithString:text
                                                                                 attributes:@{
        NSFontAttributeName: [UIFont systemFontOfSize:15],
        NSForegroundColorAttributeName: UIColor.blackColor
    }];

    // === Highlight the status substring ===
    UIColor *statusColor = [self colorForStatus:status];
    NSRange statusRange = [text rangeOfString:status options:NSCaseInsensitiveSearch];
    if (statusRange.location != NSNotFound) {
        [attrText addAttributes:@{
            NSFontAttributeName: [UIFont boldSystemFontOfSize:15],
            NSForegroundColorAttributeName: statusColor
        } range:statusRange];
    }

    return attrText;
}

- (NSString *)formatDate:(NSString *)inputDateString {
    if (!inputDateString || inputDateString.length == 0) return @"--/--/----";
    NSDateFormatter *inputFmt = [[NSDateFormatter alloc] init];
    inputFmt.dateFormat = @"yyyy-MM-dd HH:mm:ss";
    NSDate *date = [inputFmt dateFromString:inputDateString];
    if (!date) return inputDateString;
    NSDateFormatter *outputFmt = [[NSDateFormatter alloc] init];
    outputFmt.dateFormat = @"dd/MM/yyyy";
    return [outputFmt stringFromDate:date];
}

#pragma mark - Individual Card Taps
- (void)handleAnnouncementTap:(UITapGestureRecognizer *)gesture {
    UIView *card = gesture.view;
    [self animateTapForView:card];
    NSLog(@"📰 Announcement card tapped");
    // TODO: open AnnouncementDetailsScreen equivalent in future
}

- (void)handleAppointmentTap:(UITapGestureRecognizer *)gesture {
    UIView *card = gesture.view;
    [self animateTapForView:card];

    NSDictionary *appointmentData = objc_getAssociatedObject(card, "appointmentData");

    if (!appointmentData) {
        NSLog(@"⚠️ No appointment data found");
        return;
    }

    NSLog(@"📅 Opening AppointmentDetails with: %@", appointmentData);

    AppointmentDetailsViewController *vc = [[AppointmentDetailsViewController alloc] init];
    vc.modalPresentationStyle = UIModalPresentationFullScreen;
    vc.appointmentData = appointmentData;
    [self presentViewController:vc animated:YES completion:nil];
}


- (void)handleIssueTap:(UITapGestureRecognizer *)gesture {
    UIView *tappedCard = gesture.view;
    NSDictionary *issueData = objc_getAssociatedObject(tappedCard, "issueData");

    if (issueData && [issueData isKindOfClass:[NSDictionary class]]) {
        NSLog(@"🧭 Navigating to IssueDetailViewController with issue: %@", issueData);

        IssueDetailViewController *nextVC = [[IssueDetailViewController alloc] init];
        nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
        nextVC.issueData = issueData;
        nextVC.projectCode = self.projectCode;
        nextVC.projectName = self.projectName;
        [self presentViewController:nextVC animated:YES completion:nil];
    } else {
        NSLog(@"⚠️ No valid issue data found for tapped card: %@", issueData);
    }
}

#pragma mark - Card Factory with Tap Animation
- (UIView *)createCardWithText:(NSString *)text frame:(CGRect)frame {
    UIView *card = [[UIView alloc] initWithFrame:frame];
    card.backgroundColor = [UIColor whiteColor];
    card.layer.cornerRadius = 8;
    card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
    card.layer.shadowOpacity = 0.8;
    card.layer.shadowOffset = CGSizeMake(0, 1);
    card.userInteractionEnabled = YES;

    UILabel *body = [[UILabel alloc] initWithFrame:CGRectMake(12, 12, frame.size.width - 24, frame.size.height - 24)];
    body.text = text;
    body.numberOfLines = 2;
    body.font = [UIFont systemFontOfSize:15];
    [card addSubview:body];

    // Add tap gesture with animation
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cardTapped:)];
    [card addGestureRecognizer:tap];

    return card;
}

#pragma mark - Tap Handlers
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gesture {
    UILabel *header = (UILabel *)gesture.view;
    [self animateTapForView:header];
    NSLog(@"📘 Section tapped: %@", header.text);
}

-(void)appointmentHeaderTapped:(UITapGestureRecognizer *)gesture {
    AppointmentViewMoreViewController *nextVC = [[AppointmentViewMoreViewController alloc] init];
    nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:nextVC animated:YES completion:nil];
}

-(void)issueHeaderTapped:(UITapGestureRecognizer *)gesture {
    IssuesViewController *nextVC = [[IssuesViewController alloc] init];
    nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
    nextVC.projectCode = self.projectCode;
    nextVC.projectName = self.projectName;
    [self presentViewController:nextVC animated:YES completion:nil];
}

- (void)cardTapped:(UITapGestureRecognizer *)gesture {
    UIView *card = gesture.view;
    [self animateTapForView:card];
    NSLog(@"🟩 Card tapped: %@", ((UILabel *)card.subviews.firstObject).text);
}

- (void)animateTapForView:(UIView *)view {
    [UIView animateWithDuration:0.1 animations:^{
        view.transform = CGAffineTransformMakeScale(0.96, 0.96);
        view.alpha = 0.8;
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.1 animations:^{
            view.transform = CGAffineTransformIdentity;
            view.alpha = 1.0;
        }];
    }];
}

-(void)infoButtonTapped{
  GeneralInfoViewController *nextVC = [[GeneralInfoViewController alloc] init];
  nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
  nextVC.unitID = self.projectCode;
  nextVC.unitName = self.projectName;
  [self presentViewController:nextVC animated:YES completion:nil];
}

#pragma mark - Loading Overlay
- (void)showLoadingOverlay:(NSString *)message {
    if (self.loadingOverlay.superview) return; // already showing

    UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
    overlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
    overlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

    UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]
                                        initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
    spinner.center = overlay.center;
    spinner.color = UIColor.whiteColor;
    [spinner startAnimating];

    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(spinner.frame) + 16,
                                                               overlay.bounds.size.width, 30)];
    label.text = message ?: @"Loading...";
    label.textAlignment = NSTextAlignmentCenter;
    label.textColor = UIColor.whiteColor;
    label.font = [UIFont boldSystemFontOfSize:16];

    [overlay addSubview:spinner];
    [overlay addSubview:label];
    [self.view addSubview:overlay];

    self.loadingOverlay = overlay;
    self.spinner = spinner;
}

- (void)hideLoadingOverlay {
    [self.spinner stopAnimating];
    [self.loadingOverlay removeFromSuperview];
    self.loadingOverlay = nil;
    self.spinner = nil;
}

- (void)handleMenuButtonTapped {
    NSLog(@"🍔 Menu button tapped");
    if (self.drawerController) {
        [self.drawerController toggleDrawer];
    } else {
        NSLog(@"⚠️ drawerController not set on DashboardViewController");
    }
}

- (void)onBack {
    [self.navigationController popViewControllerAnimated:YES];
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetY = scrollView.contentOffset.y;
    
    // Parallax effect for the header image
    if (offsetY < 0) {
        self.backgroundImageView.transform = CGAffineTransformMakeTranslation(0, offsetY / 2);
    } else {
        self.backgroundImageView.transform = CGAffineTransformIdentity;
    }
    
    // Gradually fade in sticky bar
    CGFloat fadeStart = 0;
    CGFloat fadeEnd = 100; // how far scroll before fully opaque
    CGFloat alpha = MIN(MAX((offsetY - fadeStart) / (fadeEnd - fadeStart), 0), 1);
    self.stickyBar.backgroundColor = [UIColor colorWithRed:0 green:0.43 blue:0.55 alpha:alpha];
}

@end
