/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

#import <XCTest/XCTest.h>

#import <React/RCTBridge.h>
#import <React/RCTImageLoader.h>

#import "RCTImageLoaderHelpers.h"

unsigned char blackGIF[] = {0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0x00,
                            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x2c, 0x00, 0x00, 0x00, 0x00,
                            0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b};

RCTDefineImageURLLoader(RCTImageLoaderTestsURLLoader1) RCTDefineImageURLLoader(RCTImageLoaderTestsURLLoader2)
    RCTDefineImageDecoder(RCTImageLoaderTestsDecoder1) RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)

        @interface RCTImageLoaderTests : XCTestCase

@end

@implementation RCTImageLoaderTests {
  NSURL *_bundleURL;
}

- (void)setUp
{
  NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  _bundleURL = [bundle URLForResource:@"RNTesterUnitTestsBundle" withExtension:@"js"];
}

- (void)testImageLoading
{
  UIImage *image = [UIImage new];

  id<RCTImageURLLoader> loader = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0
      canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) {
        return YES;
      }
      loadImageURLHandler:^RCTImageLoaderCancellationBlock(
          __unused NSURL *imageURL,
          __unused CGSize size,
          __unused CGFloat scale,
          __unused RCTResizeMode resizeMode,
          RCTImageLoaderProgressBlock progressHandler,
          RCTImageLoaderCompletionBlock completionHandler) {
        progressHandler(1, 1);
        completionHandler(nil, image);
        return nil;
      }];

  NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL
                                                                        moduleProvider:^{
                                                                          return @[ loader ];
                                                                        }
                                                                         launchOptions:nil];

  NSURLRequest *urlRequest =
      [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://reactnative.dev/img/opengraph.png"]];
  [[bridge moduleForClass:[RCTImageLoader class]] loadImageWithURLRequest:urlRequest
      size:CGSizeMake(100, 100)
      scale:1.0
      clipped:YES
      resizeMode:RCTResizeModeContain
      progressBlock:^(int64_t progress, int64_t total) {
        XCTAssertEqual(progress, 1);
        XCTAssertEqual(total, 1);
      }
      partialLoadBlock:^(UIImage *loadedImage) {
      }
      completionBlock:^(NSError *loadError, id loadedImage) {
        XCTAssertEqualObjects(loadedImage, image);
        XCTAssertNil(loadError);
      }];
}

- (void)testImageLoaderUsesImageURLLoaderWithHighestPriority
{
  UIImage *image = [UIImage new];

  id<RCTImageURLLoader> loader1 = [[RCTImageLoaderTestsURLLoader1 alloc] initWithPriority:1.0
      canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) {
        return YES;
      }
      loadImageURLHandler:^RCTImageLoaderCancellationBlock(
          __unused NSURL *imageURL,
          __unused CGSize size,
          __unused CGFloat scale,
          __unused RCTResizeMode resizeMode,
          RCTImageLoaderProgressBlock progressHandler,
          RCTImageLoaderCompletionBlock completionHandler) {
        progressHandler(1, 1);
        completionHandler(nil, image);
        return nil;
      }];

  id<RCTImageURLLoader> loader2 = [[RCTImageLoaderTestsURLLoader2 alloc] initWithPriority:0.5
      canLoadImageURLHandler:^BOOL(__unused NSURL *requestURL) {
        return YES;
      }
      loadImageURLHandler:^RCTImageLoaderCancellationBlock(
          __unused NSURL *imageURL,
          __unused CGSize size,
          __unused CGFloat scale,
          __unused RCTResizeMode resizeMode,
          __unused RCTImageLoaderProgressBlock progressHandler,
          __unused RCTImageLoaderCompletionBlock completionHandler) {
        XCTFail(@"Should not have used loader2");
        return nil;
      }];

  NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL
                                                                        moduleProvider:^{
                                                                          return @[ loader1, loader2 ];
                                                                        }
                                                                         launchOptions:nil];

  NSURLRequest *urlRequest =
      [NSURLRequest requestWithURL:[NSURL URLWithString:@"https://reactnative.dev/img/opengraph.png"]];
  [[bridge moduleForClass:[RCTImageLoader class]] loadImageWithURLRequest:urlRequest
      size:CGSizeMake(100, 100)
      scale:1.0
      clipped:YES
      resizeMode:RCTResizeModeContain
      progressBlock:^(int64_t progress, int64_t total) {
        XCTAssertEqual(progress, 1);
        XCTAssertEqual(total, 1);
      }
      partialLoadBlock:^(UIImage *loadedImage) {
      }
      completionBlock:^(NSError *loadError, id loadedImage) {
        XCTAssertEqualObjects(loadedImage, image);
        XCTAssertNil(loadError);
      }];
}

- (void)testImageDecoding
{
  NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
  UIImage *image = [[UIImage alloc] initWithData:data];

  id<RCTImageDataDecoder> decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0
      canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
        return YES;
      }
      decodeImageDataHandler:^RCTImageLoaderCancellationBlock(
          NSData *imageData,
          __unused CGSize size,
          __unused CGFloat scale,
          __unused RCTResizeMode resizeMode,
          RCTImageLoaderCompletionBlock completionHandler) {
        XCTAssertEqualObjects(imageData, data);
        completionHandler(nil, image);
        return nil;
      }];

  NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL
                                                                        moduleProvider:^{
                                                                          return @[ decoder ];
                                                                        }
                                                                         launchOptions:nil];

  RCTImageLoaderCancellationBlock cancelBlock =
      [[bridge moduleForClass:[RCTImageLoader class]] decodeImageData:data
                                                                 size:CGSizeMake(1, 1)
                                                                scale:1.0
                                                              clipped:NO
                                                           resizeMode:RCTResizeModeStretch
                                                      completionBlock:^(NSError *decodeError, id decodedImage) {
                                                        XCTAssertEqualObjects(decodedImage, image);
                                                        XCTAssertNil(decodeError);
                                                      }];
  XCTAssertNotNil(cancelBlock);
}

- (void)testImageLoaderUsesImageDecoderWithHighestPriority
{
  NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
  UIImage *image = [[UIImage alloc] initWithData:data];

  id<RCTImageDataDecoder> decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0
      canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
        return YES;
      }
      decodeImageDataHandler:^RCTImageLoaderCancellationBlock(
          NSData *imageData,
          __unused CGSize size,
          __unused CGFloat scale,
          __unused RCTResizeMode resizeMode,
          RCTImageLoaderCompletionBlock completionHandler) {
        XCTAssertEqualObjects(imageData, data);
        completionHandler(nil, image);
        return nil;
      }];

  id<RCTImageDataDecoder> decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5
      canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
        return YES;
      }
      decodeImageDataHandler:^RCTImageLoaderCancellationBlock(
          __unused NSData *imageData,
          __unused CGSize size,
          __unused CGFloat scale,
          __unused RCTResizeMode resizeMode,
          __unused RCTImageLoaderCompletionBlock completionHandler) {
        XCTFail(@"Should not have used decoder2");
        return nil;
      }];

  NS_VALID_UNTIL_END_OF_SCOPE RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_bundleURL
                                                                        moduleProvider:^{
                                                                          return @[ decoder1, decoder2 ];
                                                                        }
                                                                         launchOptions:nil];

  RCTImageLoaderCancellationBlock cancelBlock =
      [[bridge moduleForClass:[RCTImageLoader class]] decodeImageData:data
                                                                 size:CGSizeMake(1, 1)
                                                                scale:1.0
                                                              clipped:NO
                                                           resizeMode:RCTResizeModeStretch
                                                      completionBlock:^(NSError *decodeError, id decodedImage) {
                                                        XCTAssertEqualObjects(decodedImage, image);
                                                        XCTAssertNil(decodeError);
                                                      }];
  XCTAssertNotNil(cancelBlock);
}

@end
