/**

 * Copyright (c) 2024 Huawei Technologies Co., Ltd.

 *

 * This source code is licensed under the MIT license found in the

 * LICENSE-MIT file in the root directory of this source tree.

 */



import { Image, ImageSourcePropType, ScrollView, Text, View } from 'react-native';

import { TestCase, TestSuite } from '@rnoh/testerino';

import React from 'react';

import { Button } from '../components';



const WRONG_IMAGE_SRC = 'not_image';

const LOCAL_IMAGE_ASSET_ID = require('../assets/pravatar-131.jpg');

const REMOTE_IMAGE_URL = 'https://i.pravatar.cc/100?img=31';

const LARGE_REMOTE_IMAGE_URL =

  'https://images.unsplash.com/photo-1556740749-887f6717d7e4';

const REMOTE_REDIRECT_IMAGE_URL = 'http://placeholder.com/350x150';

const REMOTE_GIF_URL =

  'https://upload.wikimedia.org/wikipedia/commons/2/2c/Rotating_earth_%28large%29.gif';

const DATA_URI =

  'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';



export const ImageTest = () => {

  return (

    <TestSuite name="Image">

      <TestCase itShould="support loading local images">

        <Image

          style={{ borderRadius: 8, borderWidth: 1 }}

          source={LOCAL_IMAGE_ASSET_ID}

        />

      </TestCase>

      <ImageExampleCase

        itShould="support loading remote images"

        source={{ uri: REMOTE_IMAGE_URL }}

      />

      <ImageExampleCase

        itShould="support loading remote images (with http redirect)"

        source={{ uri: REMOTE_REDIRECT_IMAGE_URL }}

      />

      <ImageExampleCase

        itShould="support loading large remote images (over 5mb)"

        source={{ uri: LARGE_REMOTE_IMAGE_URL }}

      />

      <ImageExampleCase

        itShould="support loading image data uris"

        source={{ uri: DATA_URI }}

      />

      <ImageExampleCase

        itShould="support loading remote animated gifs"

        source={{ uri: REMOTE_GIF_URL }}

      />

      <TestCase itShould="display alt when the image doesn't load">

        <View>

          <Image

            source={require('../assets/fonts/Pacifico-Regular.ttf')}

            alt="This image could not be loaded!"

          />

        </View>

      </TestCase>

      <TestCase

        itShould="retrieve remote image size"

        fn={({ expect }) => {

          return new Promise((resolve, reject) => {

            Image.getSize(

              REMOTE_IMAGE_URL,

              (width, height) => {

                expect(width).to.be.eq(100);

                expect(height).to.be.eq(100);

                resolve();

              },

              e => {

                reject(e);

              },

            );

          });

        }}

      />

      <TestCase

        itShould="retrieve local image size"

        fn={({ expect }) => {

          const resolvedAsset = Image.resolveAssetSource(LOCAL_IMAGE_ASSET_ID);

          expect(resolvedAsset.width).to.be.eq(150);

          expect(resolvedAsset.height).to.be.eq(150);

        }}

      />

      <TestCase

        itShould="prefetch image"

        fn={async ({ expect }) => {

          let ex: any;

          try {

            await Image.prefetch(WRONG_IMAGE_SRC);

          } catch (e) {

            ex = e;

          }

          expect(ex).to.be.not.undefined;

          expect(await Image.prefetch(REMOTE_IMAGE_URL)).to.be.true;

          expect(await Image.prefetch(REMOTE_IMAGE_URL)).to.be.true;

        }}

      />

      <TestCase

        itShould="query cache"

        fn={async ({ expect }) => {

          await Image.prefetch(REMOTE_IMAGE_URL);

          expect(Image.queryCache).not.to.be.undefined;

          const result = await Image.queryCache?.([

            REMOTE_IMAGE_URL,

            WRONG_IMAGE_SRC,

          ]);

          expect(result).to.be.not.undefined;

          expect(result?.[REMOTE_IMAGE_URL]).to.be.not.undefined;

          expect(result?.[REMOTE_IMAGE_URL]).to.be.eq('disk');

          expect(result?.[WRONG_IMAGE_SRC]).to.be.undefined;

        }}

      />

      <TestCase

        skip // https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/246

        itShould="render circular image on a red rectangle (overlayColor)">

        <Image

          source={LOCAL_IMAGE_ASSET_ID}

          style={{ overlayColor: 'red', borderRadius: Number.MAX_SAFE_INTEGER }}

        />

      </TestCase>

      <TestCase

        itShould="call onLoadStart"

        initialState={'not called'}

        arrange={({ setState }) => {

          return (

            <Image

              source={LOCAL_IMAGE_ASSET_ID}

              onLoadStart={() => setState('called')}

            />

          );

        }}

        assert={({ expect, state }) => {

          expect(state).to.be.eq('called');

        }}

      />

      <TestCase

        itShould="call onLoad"

        initialState={{}}

        arrange={({ setState, state }) => {

          return (

            <View>

              <Text>{JSON.stringify(state)}</Text>

              <Image

                source={LOCAL_IMAGE_ASSET_ID}

                onLoad={event => {

                  setState(event.nativeEvent.source);

                }}

              />

            </View>

          );

        }}

        assert={({ expect, state }) => {

          expect(state).to.contain.all.keys('width', 'height', 'uri');

        }}

      />

      <TestCase

        itShould="call onError (local)"

        initialState={null}

        arrange={({ setState, state }) => {

          return (

            <View>

              <Text>{JSON.stringify(state)}</Text>

              <Image

                source={require('../assets/fonts/Pacifico-Regular.ttf')}

                onError={event => {

                  setState(event.nativeEvent.error);

                }}

              />

            </View>

          );

        }}

        assert={({ expect, state }) => {

          expect(state).to.be.not.null;

        }}

      />

      <TestCase

        itShould="call onError (remote)"

        initialState={null}

        arrange={({ setState, state }) => {

          return (

            <View>

              <Text>{JSON.stringify(state)}</Text>

              <Image

                source={{ uri: 'https://www.google.com/image' }}

                onError={event => {

                  setState(event.nativeEvent.error);

                }}

              />

            </View>

          );

        }}

        assert={({ expect, state }) => {

          expect(state).to.be.not.null;

        }}

      />

      <TestSuite

        name="resizeMode" // https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/245

      >

        <TestCase itShould="render small image in the center (center)">

          <Image

            style={{ width: '100%', height: 100 }}

            source={LOCAL_IMAGE_ASSET_ID}

            resizeMode="center"

          />

        </TestCase>

        <TestCase itShould="render image touching top and bottom edges in the center (contain)">

          <Image

            style={{ width: '100%', height: 100 }}

            source={LOCAL_IMAGE_ASSET_ID}

            resizeMode="contain"

          />

        </TestCase>

        <TestCase itShould="fully cover test case area while preserving aspect ratio (cover)">

          <Image

            style={{ width: '100%', height: 100 }}

            source={LOCAL_IMAGE_ASSET_ID}

            resizeMode="cover"

          />

        </TestCase>

        <TestCase itShould="cover test case area by repeating image (repeat)">

          <Image

            style={{ width: '100%', height: 100 }}

            source={LOCAL_IMAGE_ASSET_ID}

            resizeMode="repeat"

          />

        </TestCase>

        <TestCase itShould="cover test case area by stretching (stretch)">

          <Image

            style={{ width: '100%', height: 100 }}

            source={LOCAL_IMAGE_ASSET_ID}

            resizeMode="stretch"

          />

        </TestCase>

      </TestSuite>

      <TestSuite name="blurRadius">

        <TestCase itShould="blur images with various blur radius">

          <View style={{ flexDirection: 'row', flexWrap: 'wrap' }}>

            <Image

              style={{ width: 64, height: 64, margin: 4 }}

              source={LOCAL_IMAGE_ASSET_ID}

              blurRadius={0}

            />

            <Image

              style={{ width: 64, height: 64, margin: 4 }}

              source={LOCAL_IMAGE_ASSET_ID}

              blurRadius={5}

            />

            <Image

              style={{ width: 64, height: 64, margin: 4 }}

              source={LOCAL_IMAGE_ASSET_ID}

              blurRadius={10}

            />

            <Image

              style={{ width: 64, height: 64, margin: 4 }}

              source={LOCAL_IMAGE_ASSET_ID}

              blurRadius={15}

            />

            <Image

              style={{ width: 64, height: 64, margin: 4 }}

              source={LOCAL_IMAGE_ASSET_ID}

              blurRadius={20}

            />

            <Image

              style={{ width: 64, height: 64, margin: 4 }}

              source={LOCAL_IMAGE_ASSET_ID}

              blurRadius={25}

            />

          </View>

        </TestCase>

      </TestSuite>

      <TestCase itShould="replace opaque pixels with the green color (tintColor)">

        <View

          style={{

            flex: 1,

            flexDirection: 'row',

            justifyContent: 'space-around',

          }}>

          <Image

            source={require('../assets/expo.png')}

            style={{

              width: 100,

              height: 100,

            }}

          />

          <Image

            source={require('../assets/expo.png')}

            style={{

              width: 100,

              height: 100,

              tintColor: 'green',

            }}

          />

        </View>

      </TestCase>

      <TestCase modal itShould="stop displaying on press">

        <SwitchSourceTest />

      </TestCase>

      <TestCase itShould="render top image in a bit lower quality (difference barely visible)">

        <Image

          style={{ width: 200, height: 200 }}

          source={require('../assets/noise.png')}

          resizeMethod="resize"

          resizeMode="stretch"

        />

        <View style={{ height: 10 }} />

        <Image

          style={{ width: 200, height: 200 }}

          source={require('../assets/noise.png')}

          resizeMethod="scale"

          resizeMode="stretch"

        />

      </TestCase>

      <TestCase

        modal

        skip // https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/483

        itShould="fade images with varying durations">

        <View style={{ flexDirection: 'row', gap: 24 }}>

          <View style={{ width: 100 }}>

            <Image

              // HACK: ?v=Date.now() is used to prevent caching

              // - cached images are not fading/faded in

              source={{ uri: REMOTE_IMAGE_URL + '?v=' + Date.now() }}

              style={{ width: 100, height: 100, borderRadius: 8 }}

              fadeDuration={0}

            />

            <Text>This image will fade in over the time of 0s.</Text>

          </View>

          <View style={{ width: 100 }}>

            <Image

              source={{ uri: REMOTE_IMAGE_URL + '?v=' + Date.now() }}

              style={{ width: 100, height: 100, borderRadius: 8 }}

              fadeDuration={1500}

            />

            <Text>This image will fade in over the time of 1.5s.</Text>

          </View>

          <View style={{ width: 100 }}>

            <Image

              source={{ uri: REMOTE_IMAGE_URL + '?v=' + Date.now() }}

              style={{ width: 100, height: 100, borderRadius: 8 }}

              fadeDuration={5000}

            />

            <Text>This image will fade in over the time of 5s.</Text>

          </View>

        </View>

        {/* To test local fadeDuration for localImages you have to disable caching */}

        <View style={{ flexDirection: 'row', gap: 24 }}>

          <View style={{ width: 100 }}>

            <Image

              source={LOCAL_IMAGE_ASSET_ID}

              style={{ width: 100, height: 100, borderRadius: 8 }}

              fadeDuration={0}

            />

            <Text>This image will fade in over the time of 0s.</Text>

          </View>

          <View style={{ width: 100 }}>

            <Image

              source={LOCAL_IMAGE_ASSET_ID}

              style={{ width: 100, height: 100, borderRadius: 8 }}

              fadeDuration={1500}

            />

            <Text>This image will fade in over the time of 1.5s.</Text>

          </View>

          <View style={{ width: 100 }}>

            <Image

              source={LOCAL_IMAGE_ASSET_ID}

              style={{ width: 100, height: 100, borderRadius: 8 }}

              fadeDuration={5000}

            />

            <Text>This image will fade in over the time of 5s.</Text>

          </View>

        </View>

      </TestCase>

      <TestCase

        modal

        itShould="load many large images without causing out-of-memory issues">

        <ScrollView>

          {Array.from({ length: 25 }, (_, index) => (

            <Image

              key={index}

              style={{ width: 200, height: 200 }}

              source={{ uri: LARGE_REMOTE_IMAGE_URL }}

            />

          ))}

        </ScrollView>

      </TestCase>

    </TestSuite>

  );

};



const ImageExampleCase = ({

  itShould,

  source,

}: {

  itShould: string;

  source: ImageSourcePropType;

}) => (

  <TestCase itShould={itShould}>

    <Image

      style={{ borderRadius: 8, borderWidth: 1, height: 150 }}

      source={source}

      onError={e => console.error(e.nativeEvent.error)}

    // resizeMode="contain"

    />

  </TestCase>

);



const SwitchSourceTest = () => {

  const SOURCES = [

    REMOTE_IMAGE_URL,

    '',

    REMOTE_REDIRECT_IMAGE_URL,

    WRONG_IMAGE_SRC,

  ];



  const [idx, setIdx] = React.useState(0);



  return (

    <View>

      <View style={{ flexDirection: 'row' }}>

        <Image source={{ uri: SOURCES[idx] }} style={{ width: 100, height: 100 }} />

        <Text>{`Source: ${SOURCES[idx]}`}</Text>

      </View>

      <Button

        label="Switch Source"

        onPress={() => {

          setIdx(i => (i + 1) % SOURCES.length);

        }}

      />

    </View>

  );

};