/**
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import React, {createRef, useRef} from 'react';
import {
  View,
  StyleSheet,
  Text,
  ScrollView,
  UIManager,
  findNodeHandle,
} from 'react-native';
import {TestSuite} from '@rnoh/testerino';
import {Effect, Ref, Button, TestCase} from '../components';

export function UIManagerTest() {
  return (
    <TestSuite name="UIManager.measure">
      <TestCase.Automated
        itShould="not round text measurement to integers"
        initialState={{
          view: {width: 0, height: 0},
          text: {width: 0, height: 0},
        }}
        arrange={({setState, done}) => {
          return (
            <Ref<View>
              render={refView => {
                return (
                  <Ref<Text>
                    render={refText => {
                      return (
                        <Effect
                          onMount={() => {
                            setTimeout(() => {
                              refText.current?.measure(
                                (x, y, width, height, _left, _top) => {
                                  setState(prev => ({
                                    ...prev,
                                    text: {width, height},
                                  }));
                                },
                              );
                              refView.current?.measure(
                                (x, y, width, height, _left, _top) => {
                                  setState(prev => ({
                                    ...prev,
                                    view: {width, height},
                                  }));
                                },
                              );
                              done();
                            }, 100);
                          }}>
                          <Text
                            ref={refText}
                            style={{
                              width: 256.5,
                              height: 64.5,
                              color: '#fff',
                              borderColor: '#FF8041',
                              textAlign: 'center',
                              textAlignVertical: 'center',
                              justifyContent: 'center',
                              alignItems: 'center',
                              backgroundColor: '#7700FF',
                            }}>
                            <View
                              ref={refView}
                              style={{
                                backgroundColor: 'red',
                                width: 16.5,
                                height: 16.5,
                              }}
                            />
                            {'Measure me'}
                          </Text>
                        </Effect>
                      );
                    }}
                  />
                );
              }}
            />
          );
        }}
        act={() => {}}
        assert={({expect, state}) => {
          expect(state.view.width).to.be.closeTo(16.5, 0.49);
          expect(state.view.width).not.to.be.eq(16);
          expect(state.view.width).not.to.be.eq(17);
          expect(state.text.width).to.be.closeTo(256.5, 0.49);
          expect(state.text.width).not.to.be.eq(256);
          expect(state.text.width).not.to.be.eq(257);
          expect(state.text.height).to.be.closeTo(64.5, 0.49);
          expect(state.text.height).not.to.be.eq(65);
          expect(state.text.height).not.to.be.eq(64);
        }}
      />
      <TestCase.Example itShould="scroll down on press">
        <DispatchCommandTest />
      </TestCase.Example>
      <TestCase.Logical
        itShould="return view manager config"
        fn={({expect}) => {
          expect(UIManager.getViewManagerConfig('RCTView')).to.be.an('object');
        }}
      />
      <TestCase.Logical
        itShould="not return view manager config for non-existing view"
        fn={({expect}) => {
          expect(UIManager.getViewManagerConfig('RCTNotAView')).to.be.null;
        }}
      />
      <TestCase.Logical
        itShould="check if view manager config exists"
        fn={({expect}) => {
          expect(UIManager.hasViewManagerConfig('RCTView')).to.be.true;
          expect(UIManager.hasViewManagerConfig('RCTNotAView')).to.be.false;
        }}
      />
      <TestCase.Automated
        itShould="measure the view with respect to the window"
        initialState={{
          measure: {} as {x: number; y: number; width: number; height: number},
          ref: createRef<View>(),
        }}
        arrange={({state}) => {
          return (
            <View
              style={{
                width: 20,
                height: 20,
                backgroundColor: 'blue',
              }}
              ref={state.ref}
            />
          );
        }}
        act={({state, setState, done}) => {
          setTimeout(() => {
            state.ref.current?.measureInWindow(
              (x: number, y: number, width: number, height: number) => {
                setState(prevState => ({
                  ...prevState,
                  measure: {x, y, width, height},
                }));
              },
            );
            done();
          }, 1000);
        }}
        assert={({state, expect}) => {
          expect(state.measure.width).to.equal(20);
          expect(state.measure.height).to.equal(20);
          expect(state.measure.x).to.be.greaterThan(10);
          expect(state.measure.y).to.be.greaterThan(100);
        }}
      />
      <TestCase.Manual
        itShould="measure the view with respect to the parent"
        initialState={
          {} as {x: number; y: number; width: number; height: number}
        }
        arrange={({setState}) => <MeasureLayoutTest setState={setState} />}
        assert={({state, expect}) => {
          expect(state.width).to.be.equal(20);
          expect(state.height).to.be.equal(20);
          expect(state.x).to.be.equal(10);
          expect(state.y).to.be.equal(20);
        }}
      />
      <TestCase.Manual
        itShould="not crash when calling view is descendant of"
        initialState={undefined as boolean | undefined}
        arrange={({setState}) => <ViewIsDescendantOfTest setState={setState} />}
        assert={({state, expect}) => {
          expect(state).to.be.true;
        }}
      />
    </TestSuite>
  );
}
const MeasureLayoutTest = (props: {
  setState: React.Dispatch<
    React.SetStateAction<{x: number; y: number; width: number; height: number}>
  >;
}) => {
  const ref = useRef<View>(null);
  const containerRef = useRef<View>(null);
  return (
    <>
      <View
        style={{
          width: 100,
          height: 100,
          backgroundColor: 'red',
          paddingLeft: 10,
          paddingTop: 20,
        }}
        ref={containerRef}>
        <View
          style={{
            width: 20,
            height: 20,
            backgroundColor: 'blue',
          }}
          ref={ref}
        />
      </View>
      <Button
        label="measureLayout"
        onPress={() => {
          if (ref.current && containerRef.current) {
            ref.current?.measureLayout(
              containerRef.current,
              (x, y, width, height) => {
                props.setState({x: x, y: y, width: width, height: height});
              },
              () => {
                props.setState({x: -1, y: -1, width: -1, height: -1});
              },
            );
          }
        }}
      />
    </>
  );
};

function DispatchCommandTest() {
  const ref = useRef<ScrollView>(null);

  return (
    <View>
      <ScrollView ref={ref} style={{height: 100}}>
        <View style={[styles.box, {backgroundColor: 'red'}]} />
        <View style={[styles.box, {backgroundColor: 'blue'}]} />
      </ScrollView>
      <Button
        label="scroll to bottom"
        onPress={() => {
          UIManager.dispatchViewManagerCommand(
            findNodeHandle(ref.current),
            'scrollToEnd',
            [true],
          );
        }}
      />
    </View>
  );
}

function ViewIsDescendantOfTest({
  setState,
}: {
  setState: React.Dispatch<React.SetStateAction<boolean | undefined>>;
}) {
  const parentRef = useRef<View>(null);
  const childRef = useRef<View>(null);

  return (
    <View>
      <View
        ref={parentRef}
        collapsable={false}
        style={{height: 100, width: 100, backgroundColor: 'blue'}}>
        <View
          ref={childRef}
          collapsable={false}
          style={[styles.box, {backgroundColor: 'red'}]}
        />
      </View>
      <Button
        label="call viewIsDescendantOf"
        onPress={() => {
          // the method is not exported in the interface
          try {
            (UIManager as any).viewIsDescendantOf(
              findNodeHandle(childRef.current),
              findNodeHandle(parentRef.current),
              (isDescendantOf: boolean) => {
                console.log(isDescendantOf);
              },
            );
          } catch {
            setState(false);
          }
          setState(true);
        }}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    width: 100,
    height: 20,
    backgroundColor: 'red',
  },
  box: {
    height: 100,
    width: 50,
  },
  text: {
    height: 60,
  },
});