/**
 * 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 {
  ReturnKeyType,
  ReturnKeyTypeAndroid,
  StyleSheet,
  Text,
  TextInput,
  TextInputProps,
  View,
} from 'react-native';
import { TestSuite, TestCase } from '@rnoh/testerino';
import { useState, useRef } from 'react';
import { Button, Effect, StateKeeper } from '../components';

export function TextInputTest() {
  return (
    <TestSuite name="TextInput">
      <TestCase
        modal
        itShould="render textinput and change the text component based on the values inputted">
        <TextInputWithText style={styles.textInput} />
      </TestCase>
      <TestCase modal itShould="render non-editable textInput">
        <TextInputWithText style={styles.textInput} editable={false} />
      </TestCase>
      <TestCase modal itShould="render textInput with Pacifico Regular font">
        <TextInputWithText
          style={[styles.textInput, { fontFamily: 'Pacifico-Regular' }]}
        />
      </TestCase>
      <TestCase modal itShould="render textInput with caret hidden">
        <TextInputWithText style={styles.textInput} caretHidden />
      </TestCase>
      <TestSuite name="focus/blur">
        <TestCase
          modal
          itShould="blur text on submit (singleline)"
          initialState={false}
          arrange={({ setState }) => {
            return (
              <>
                <TextInputWithText
                  style={styles.textInput}
                  blurOnSubmit
                  onBlur={() => setState(true)}
                />
              </>
            );
          }}
          assert={({ expect, state }) => {
            expect(state).to.be.true;
          }}
        />
        <TestCase
          modal
          itShould="blur text after switching to another textinput"
          initialState={false}
          arrange={({ setState }) => {
            return (
              <>
                <TextInputWithText
                  style={styles.textInput}
                  onBlur={() => setState(true)}
                />
                <TextInputWithText
                  style={styles.textInput}
                  onBlur={() => setState(true)}
                />
              </>
            );
          }}
          assert={({ expect, state }) => {
            expect(state).to.be.true;
          }}
        />
        <TestCase
          modal
          itShould="not blur text on submit"
          skip
        //https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/403
        >
          <TextInputWithText style={styles.textInput} blurOnSubmit={false} />
          <TextInputWithText
            style={styles.textInput}
            blurOnSubmit={false}
            multiline
          />
        </TestCase>
        <TestCase
          modal
          itShould="automatically focus textInput when displayed"
          initialState={false}
          arrange={({ setState }) => (
            <TextInputWithText
              style={styles.textInput}
              autoFocus
              onFocus={() => setState(true)}
            />
          )}
          assert={({ expect, state }) => {
            expect(state).to.be.true;
          }}
        />
        <TestCase
          modal
          itShould="focus textInput on click"
          initialState={false}
          arrange={({ setState }) => (
            <TextInput
              style={styles.textInput}
              onFocus={() => setState(true)}
            />
          )}
          assert={({ expect, state }) => {
            expect(state).to.be.true;
          }}
        />
        <TestCase
          modal
          itShould="focus textInput when pressing the button"
          initialState={false}
          arrange={({ setState }) => <FocusTextInputTest setState={setState} />}
          assert={({ state, expect }) => {
            expect(state).to.be.true;
          }}
        />
      </TestSuite>
      <TestCase
        modal
        itShould="render textInput with blue underline"
        skip
      //https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/404
      >
        <TextInputWithText
          style={styles.textInput}
          underlineColorAndroid={'blue'}
        />
      </TestCase>
      <TestCase modal itShould="render textinput with red placeholder">
        <TextInputWithText
          style={styles.textInput}
          placeholder="Placeholder"
          placeholderTextColor={'red'}
        />
      </TestCase>
      <TestCase modal itShould="render textinput with green selection color">
        <TextInputWithText style={styles.textInput} selectionColor="green" />
      </TestCase>
      <TestCase modal itShould="render multiline text input">
        <TextInputWithText style={styles.textInputBigger} multiline />
      </TestCase>
      <TestCase
        modal
        itShould="render multiline text input with Pacifico Regular font">
        <TextInputWithText
          style={[styles.textInputBigger, { fontFamily: 'Pacifico-Regular' }]}
          multiline
        />
      </TestCase>
      <TestCase modal itShould="render text input with maximally 10 characters">
        <TextInputWithText style={styles.textInput} maxLength={10} />
      </TestCase>
      <TestCase modal itShould="toggle between rendering 10 and 5 characters">
        <StateKeeper
          initialValue={10}
          renderContent={(maxLength, setMaxLength) => {
            return (
              <Effect
                onMount={() => {
                  const interval = setInterval(() => {
                    setMaxLength(prev => (prev === 10 ? 5 : 10));
                  }, 1000);
                  return () => {
                    clearInterval(interval);
                  };
                }}>
                <TextInputWithText
                  style={styles.textInput}
                  maxLength={maxLength}
                  value="1234567890"
                />
              </Effect>
            );
          }}
        />
      </TestCase>
      <TestCase
        modal
        itShould="toggle between different capitalization modes"
        skip
      //https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/408
      >
        <AutoCapitalize />
      </TestCase>
      <TestCase
        modal
        itShould="trigger onSubmitEditing event after submiting"
        initialState={false}
        arrange={({ setState }) => (
          <TextInputWithText
            style={styles.textInput}
            onSubmitEditing={() => setState(true)}
          />
        )}
        assert={({ expect, state }) => {
          expect(state).to.be.true;
        }}
      />
      <TestCase modal itShould="toggle between different return keys">
        <ReturnKeyTypeView />
      </TestCase>
      <TestCase modal itShould="render secure text input (text obscured)">
        <TextInputWithText style={styles.textInput} secureTextEntry />
      </TestCase>
      <TestCase
        modal
        skip
        //https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/736
        itShould="trigger onKeyPress event after pressing key (press 'A' to pass)"
        initialState={''}
        arrange={({ setState }) => (
          <TextInputWithText
            style={styles.textInput}
            autoFocus
            onKeyPress={event => setState(event.nativeEvent.key)}
          />
        )}
        assert={({ expect, state }) => {
          expect(state).to.be.eq('A');
        }}
      />
      <TestCase modal itShould="show text input with default value">
        <DefaultProps />
      </TestCase>
      <TestCase
        modal
        itShould="trigger onLayout event on mount"
        initialState={{}}
        arrange={({ setState, state }) => {
          return (
            <>
              <Text>{JSON.stringify(state)}</Text>
              <TextInput
                style={styles.textInput}
                onLayout={event => {
                  setState(event.nativeEvent.layout);
                }}
              />
            </>
          );
        }}
        assert={({ expect, state }) => {
          expect(state).to.include.all.keys('width', 'height', 'x', 'y');
        }}
      />
      <TestCase
        modal
        itShould="render textinputs with different keyboard types">
        <View>
          <TextInputKeyboardType keyboardType="default" />
          <TextInputKeyboardType keyboardType="number-pad" />
          <TextInputKeyboardType keyboardType="decimal-pad" />
          <TextInputKeyboardType keyboardType="numeric" />
          <TextInputKeyboardType keyboardType="email-address" />
          <TextInputKeyboardType keyboardType="phone-pad" />
          <TextInputKeyboardType keyboardType="url" />
        </View>
      </TestCase>
      <TestCase modal itShould="render textinput with allowFontScaling">
        <TextInputWithText
          style={styles.textInput}
          allowFontScaling
          defaultValue="Scaled"
        />
        <TextInputWithText
          style={styles.textInput}
          allowFontScaling={false}
          defaultValue="Not scaled"
        />
        <TextInputWithText
          style={[styles.textInput, { fontSize: 40 }]}
          allowFontScaling={true}
          defaultValue="Scaled big"
        />
        <TextInputWithText
          style={[styles.textInput, { fontSize: 40 }]}
          allowFontScaling={false}
          defaultValue="Not scaled big"
        />
      </TestCase>
      <TestCase itShould="show textInput with padding" modal>
        <View style={{ width: 300, height: 200 }}>
          <TextInputWithText
            style={{
              paddingLeft: 10,
              paddingTop: 20,
              paddingRight: 30,
              paddingBottom: 40,
              backgroundColor: 'red',
            }}
          />
        </View>
      </TestCase>
      <TestCase itShould="show textInput multiline with padding" modal>
        <View style={{ width: 300, height: 200 }}>
          <TextInputWithText
            style={{
              paddingLeft: 10,
              paddingTop: 20,
              paddingRight: 30,
              paddingBottom: 40,
              backgroundColor: 'red',
              height: 100,
            }}
            multiline
          />
        </View>
      </TestCase>
      <TestCase modal itShould="render textinput with readonly">
        {/* @ts-ignore */}
        <TextInputWithText style={styles.textInput} readOnly />
      </TestCase>
      <TestCase
        modal
        itShould="display bold, italic, large placeholder with a custom font">
        <TextInput
          style={{
            fontFamily: 'StintUltraCondensed-Regular',
            fontWeight: 'bold',
            fontSize: 24,
            fontStyle: 'italic',
          }}
          placeholder="placeholder"
        />
      </TestCase>
      <TestCase modal itShould="render textinput with red text color">
        <TextInputWithText style={[styles.textInput, { color: 'red' }]} />
      </TestCase>
      <TestCase modal itShould="clear text on focus">
        {/* iOS only */}
        <TextInputWithText style={styles.textInput} clearTextOnFocus />
      </TestCase>
    </TestSuite>
  );
}
const FocusTextInputTest = (props: {
  setState: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
  const ref = useRef<TextInput>(null);
  return (
    <View>
      <Button label="focus text input" onPress={() => ref.current?.focus()} />
      <TextInput onFocus={() => props.setState(true)} ref={ref} />
    </View>
  );
};

const TextInputKeyboardType = (props: TextInputProps) => {
  return (
    <>
      <Text>{props.keyboardType}</Text>
      <TextInput
        style={{ ...styles.textInputSmall, marginBottom: 10 }}
        keyboardType={props.keyboardType}
      />
    </>
  );
};
const TextInputWithText = (props: TextInputProps) => {
  const [text, onChangeText] = useState(props.defaultValue ?? '');
  return (
    <>
      <Text style={styles.text}>{text}</Text>
      <TextInput {...props} onChangeText={onChangeText} value={text} />
    </>
  );
};
type CapitalizationType = 'none' | 'sentences' | 'words' | 'characters';
const AutoCapitalize = () => {
  const [state, setState] = useState<CapitalizationType>('none');
  const capitalizations: Array<CapitalizationType> = [
    'none',
    'sentences',
    'words',
    'characters',
  ];
  const toggleCapitalization = () => {
    const index = capitalizations.indexOf(state);
    setState(capitalizations[(index + 1) % capitalizations.length]);
  };
  return (
    <>
      <TextInputWithText style={styles.textInput} autoCapitalize={state} />
      <Button label="toggle capitalize mode" onPress={toggleCapitalization} />
      <Text>Capitalize mode: {state}</Text>
    </>
  );
};
const ReturnKeyTypeView = () => {
  const [state, setState] = useState<ReturnKeyType | ReturnKeyTypeAndroid>(
    'none',
  );
  const returnKey: Array<ReturnKeyType | ReturnKeyTypeAndroid> = [
    'none',
    'done',
    'go',
    'next',
    'search',
    'send',
    'none',
    'previous', //currently not supported by ArkUI
  ];
  const toggleReturnKey = () => {
    const index = returnKey.indexOf(state);
    setState(returnKey[(index + 1) % returnKey.length]);
  };
  return (
    <>
      <TextInputWithText style={styles.textInput} returnKeyType={state} />
      <Button label="toggle return key type" onPress={toggleReturnKey} />
      <Text>Return key: {state}</Text>
    </>
  );
};

const DefaultProps = () => {
  // @ts-ignore
  TextInput.defaultProps = {
    value: 'defaultText',
  };

  return <TextInput style={styles.textInput} />;
};

const styles = StyleSheet.create({
  container: {
    width: 80,
    height: 80,
    backgroundColor: 'red',
  },
  text: {
    width: '100%',
    height: 40,
  },
  textInputSmall: {
    height: 20, // hack
    fontSize: 8,
    color: 'black',
    backgroundColor: 'rgb(245, 240, 211)',
    borderRadius: 20,
  },
  textInput: {
    height: 40, // hack
    fontSize: 16,
    color: 'black',
    backgroundColor: 'rgb(245, 240, 211)',
    borderRadius: 40,
  },
  textInputBigger: {
    height: 80, // hack
    fontSize: 16,
    color: 'black',
    backgroundColor: 'rgb(245, 240, 211)',
    borderRadius: 20,
  },
});