#import "ATNativeUIComponent.h"
#import <AnyThinkSDK/AnyThinkSDK.h>
#import "ATNativeTool.h"
#import "ATCommonTool.h"
#import "ATNativeSelfRenderView.h"
#import "ATNativeManager.h"
#import "ATConfiguration.h"

@interface ATNativeUIComponent ()
@property (nonatomic, weak) UIView *hostView;
@property (nonatomic, copy) NSString *placementID;
@property (nonatomic, strong, nullable) ATNativeADView *nativeADView;
@property (nonatomic, strong, nullable) ATNativeSelfRenderView *selfRenderView; // Self-rendering cache to avoid duplication alloc + to facilitate unified cleanup during destroy
@property (nonatomic, assign) BOOL attached;
@property (nonatomic, assign) BOOL closedByUser;
@end

@implementation ATNativeUIComponent

- (instancetype)initWithHostView:(UIView *)hostView placementID:(NSString *)placementID {
    if ((self = [super init])) {
        _hostView = hostView;
        _placementID = [placementID copy];
      hostView.clipsToBounds = YES;
    }
    return self;
}

- (ATShowConfig *)showConfigForAttach {
    NSString *scene = [ATNativeTool scenarioIdForPlacementID:self.placementID];
    NSString *ext = [ATNativeTool showCustomExtForPlacementID:self.placementID];
    return [[ATShowConfig alloc] initWithScene:(scene ?: @"") showCustomExt:(ext ?: @"")];
}

- (void)attachIfNeeded {
    if (self.closedByUser || self.attached || self.hostView == nil || self.placementID.length == 0) {
        return;
    }
// Don't attach when hostView is not yet layout (SDK rendering with size=0 will cause problems).
// Wait until layoutSubviews is triggered before attaching (attached guard guarantees idempotence).
    if (self.hostView.bounds.size.width <= 0 || self.hostView.bounds.size.height <= 0) {
        ATLog(@"ATNativeUIComponent.attachIfNeeded hostView bounds not laid out yet (size=%@), retry on next trigger",
              NSStringFromCGSize(self.hostView.bounds.size));
        return;
    }
    ATShowConfig *cfg = [self showConfigForAttach];
    ATNativeAdOffer *offer = [[ATAdManager sharedManager] getNativeAdOfferWithPlacementID:self.placementID
                                                                              showConfig:cfg];
    if (offer == nil) {
        ATLog(@"ATNativeUIComponent.attachIfNeeded offer=nil (did JS ATNative call makeAdRequest+getNativeAd?) placementID=%@", self.placementID);
// Bottom line: offer may not be ready yet due to the timing difference between JS callbacks and Fabric layout.
// Arrange to retry after 500ms; attached guard guarantees idempotence and will not attach twice.
        __weak typeof(self) weakSelf = self;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
                       dispatch_get_main_queue(), ^{
            [weakSelf attachIfNeeded];
        });
        return;
    }

    BOOL isExpressAd = offer.nativeAd.isExpressAd;
// Key: Use the instance method of ATNativeManager to get config - its internal config.delegate=self (ATNativeManager singleton),
// Only callbacks such as didShowNativeAdInAdView/didClickNativeAdInAdView of the SDK have receivers, otherwise all will be lost.
// Directly call [ATNativeTool getATNativeADConfiguration:] to get config.delegate=nil.
    ATNativeADConfiguration *config = [[ATNativeManager sharedManager]
                                       getATNativeADConfiguration:@{} isAdaptiveHeight:isExpressAd];
// getATNativeADConfiguration internally uses extraDic to parse the width and height. Passing an empty dict will result in ADFrame=0×0.
// The SDK will not trigger didShowNativeAdInAdView: when it sees ADFrame=0×0 (the ad is considered invisible).
// Override here with the actual bounds of hostView to ensure that the SDK knows the correct rendering size.
    config.ADFrame = self.hostView.bounds;
    ATNativeADView *adView = [[ATNativeADView alloc] initWithConfiguration:config
                                                              currentOffer:offer
                                                               placementID:self.placementID];
// The frame must be set before rendererWithConfiguration::
// The SDK will use adView.bounds inside the renderer to calculate the subview layout; if bounds=0×0 at this time,
// The subviews are all squeezed to (0,0), and the template content is invisible (or cropped to 0 area).
    adView.frame = self.hostView.bounds;

    if (isExpressAd) {
// Template (Express): SDK produces the entire view by itself
        [offer rendererWithConfiguration:config selfRenderView:nil nativeADView:adView];
    } else {
// Self-render (Option B: SDK template): Use the SDK's own ATNativeSelfRenderView, and the default layout of the subview is hard-coded here.
//Hold selfRenderView as ivar: a single attach is guarded by self.attached to ensure that it is only allocated once;
// Alloc will be re-allocated only after crossing destroy and then attaching (ivar has been cleared to nil).
        if (self.selfRenderView == nil) {
            self.selfRenderView = [[ATNativeSelfRenderView alloc] initWithOffer:offer];
        }
        ATNativeSelfRenderView *selfRenderView = self.selfRenderView;
// You must also set the frame for selfRenderView itself. UIView default frame=0×0;
// CALayer rendering is not limited by bounds (subviews can still be drawn), but hit-test will be truncated at 0×0 by pointInside.
// As a result, the tap recognizer registered with the SDK on the child view will never receive touch - the advertisement "can be seen but cannot be clicked".
        selfRenderView.frame = self.hostView.bounds;
        [self applyDefaultSelfRenderLayout:selfRenderView size:self.hostView.bounds.size];

        UIView *mediaView = [adView getMediaView];
        NSMutableArray *clickable = [@[selfRenderView.iconImageView,
                                       selfRenderView.titleLabel,
                                       selfRenderView.textLabel,
                                       selfRenderView.ctaLabel,
                                       selfRenderView.mainImageView] mutableCopy];
        if (mediaView) {
            [clickable addObject:mediaView];
            selfRenderView.mediaView = mediaView;
            mediaView.frame = selfRenderView.mainImageView.frame;
            [selfRenderView addSubview:mediaView];
        }
        [adView registerClickableViewArray:clickable];

        ATNativePrepareInfo *info = [ATNativePrepareInfo loadPrepareInfo:^(ATNativePrepareInfo * _Nonnull pi) {
            pi.iconImageView = selfRenderView.iconImageView;
            pi.titleLabel = selfRenderView.titleLabel;
            pi.textLabel = selfRenderView.textLabel;
            pi.ctaLabel = selfRenderView.ctaLabel;
            pi.mainImageView = selfRenderView.mainImageView;
            pi.dislikeButton = selfRenderView.dislikeButton;
            pi.mediaView = selfRenderView.mediaView;
        }];
        [adView prepareWithNativePrepareInfo:info];
        [offer rendererWithConfiguration:config selfRenderView:selfRenderView nativeADView:adView];
// SDK will internally reset selfRenderView.frame back to 0×0 in rendererWithConfiguration:
// It must be added again after render (and the sub-view layout must also be applied again) - otherwise hit-test will be truncated at 0×0,
// Taps on child views never fire. layoutSubviews will also cover it up (see -syncLayout).
        [self syncLayout];
        adView.logoImageView.hidden = selfRenderView.isHiddenLogo;
    }

    [self.hostView addSubview:adView];
    self.nativeADView = adView;
    self.attached = YES;
    [[ATNativeManager sharedManager] registerFabricUIComponent:self forNativeADView:adView];
    ATLog(@"ATNativeUIComponent.attachIfNeeded attached placementID=%@ isExpress=%d hostView.bounds=%@",
          self.placementID, isExpressAd, NSStringFromCGRect(self.hostView.bounds));
}

/// Self-rendering default layout: refer to the common 320x250 information flow template——
/// Top: icon(L) + title(R, 2 lines)
/// Middle: mainImage/mediaView (video)
/// Bottom: CTA button (white text on blue background centered)
- (void)applyDefaultSelfRenderLayout:(ATNativeSelfRenderView *)v size:(CGSize)size {
    CGFloat pad = 8;
    CGFloat iconSide = 36;
    CGFloat headerH = iconSide;
    CGFloat ctaH = 32;
    CGFloat mediaY = pad + headerH + pad;
    CGFloat mediaH = MAX(0, size.height - mediaY - pad - ctaH - pad);

    v.iconImageView.frame = CGRectMake(pad, pad, iconSide, iconSide);
    v.titleLabel.frame = CGRectMake(pad + iconSide + pad, pad,
                                    size.width - (pad * 3 + iconSide), iconSide / 2);
    v.titleLabel.font = [UIFont boldSystemFontOfSize:14];
    v.titleLabel.numberOfLines = 1;

    v.textLabel.frame = CGRectMake(pad + iconSide + pad, pad + iconSide / 2,
                                   size.width - (pad * 3 + iconSide), iconSide / 2);
    v.textLabel.font = [UIFont systemFontOfSize:12];
    v.textLabel.textColor = [UIColor darkGrayColor];

    v.mainImageView.frame = CGRectMake(pad, mediaY, size.width - pad * 2, mediaH);
    v.mainImageView.contentMode = UIViewContentModeScaleAspectFill;
    v.mainImageView.clipsToBounds = YES;

    v.ctaLabel.frame = CGRectMake(pad, size.height - pad - ctaH, size.width - pad * 2, ctaH);
    v.ctaLabel.textAlignment = NSTextAlignmentCenter;
    v.ctaLabel.font = [UIFont boldSystemFontOfSize:14];
    v.ctaLabel.textColor = [UIColor whiteColor];
    v.ctaLabel.backgroundColor = [UIColor colorWithRed:0.20 green:0.55 blue:0.95 alpha:1.0];
    v.ctaLabel.layer.cornerRadius = 4;
    v.ctaLabel.layer.masksToBounds = YES;

    v.dislikeButton.frame = CGRectMake(size.width - pad - 18, pad, 18, 18);
}

/// The SDK will erase selfRenderView.frame back to 0×0 on multiple occasions (rendererWithConfiguration: at least once,
/// Subsequent asynchronous refresh may occur again). This method uniformly rearranges selfRenderView + each subview.
/// Caller: after render in attachIfNeeded; ATRNNativeAdView.layoutSubviews to cover up.
- (void)syncLayout {
    if (self.selfRenderView == nil || self.hostView == nil) return;
    if (self.hostView.bounds.size.width <= 0 || self.hostView.bounds.size.height <= 0) return;
    self.selfRenderView.frame = self.hostView.bounds;
    [self applyDefaultSelfRenderLayout:self.selfRenderView size:self.hostView.bounds.size];
    if (self.selfRenderView.mediaView) {
        self.selfRenderView.mediaView.frame = self.selfRenderView.mainImageView.frame;
    }
}

- (void)destroy {
    ATLog(@"ATNativeUIComponent.destroy placementID=%@ attached=%d closedByUser=%d",
          self.placementID, self.attached, self.closedByUser);
    if (self.nativeADView) {
        [[ATNativeManager sharedManager] unregisterFabricUIComponentForNativeADView:self.nativeADView];
        [[ATNativeManager sharedManager] destroyNativeAdViewIfNeeded:self.nativeADView];
        [self.nativeADView removeFromSuperview];
        self.nativeADView = nil;
    }
// selfRenderView as a adView subtree will be released with adView. Here, ivar is explicitly cleared so that the next attach can alloc again.
    self.selfRenderView = nil;
    self.attached = NO;
}

- (void)destroyFromUserClose {
    self.closedByUser = YES;
    [self destroy];
}

@end
