/**
 * 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 {TestSuite} from '@rnoh/testerino';
import {
  AccessibilityRole,
  Platform,
  Role,
  Text,
  TextInput,
  View,
} from 'react-native';
import {TestCase} from '../../components';
import React from 'react';

const ANNOUNCEMENT_BY_PLATFORM_BY_ACCESSIBILITY_ROLE = new Map<
  AccessibilityRole,
  Record<'harmony' | 'android', string | undefined>
>()
  .set('none', {android: undefined, harmony: undefined})
  .set('button', {android: 'button', harmony: 'button'})
  .set('togglebutton', {
    android: 'switch',
    harmony: 'not ticked, that is(?) button',
  })
  .set('link', {android: 'link', harmony: undefined})
  .set('search', {android: 'edit box', harmony: 'edit box'})
  .set('image', {android: 'image', harmony: 'image'})
  .set('keyboardkey', {android: 'keyboardkey', harmony: undefined})
  .set('text', {android: 'text', harmony: undefined})
  .set('adjustable', {
    android: 'slider',
    harmony: 'null %, double tap and hold with one finger...',
  })
  .set('imagebutton', {android: 'button, image', harmony: 'button'})
  .set('header', {android: 'header, heading', harmony: undefined})
  .set('summary', {android: 'summary', harmony: undefined})
  .set('alert', {android: 'alert', harmony: undefined})
  .set('checkbox', {android: 'checkbox', harmony: 'not ticked, checkbox'})
  .set('combobox', {android: 'combobox', harmony: undefined})
  .set('menu', {android: 'menu', harmony: undefined})
  .set('menubar', {android: 'menubar', harmony: undefined})
  .set('menuitem', {android: 'menuitem', harmony: undefined})
  .set('progressbar', {android: 'progressbar', harmony: 'null %, progressbar'})
  .set('radio', {android: 'radio button', harmony: 'option button'})
  .set('radiogroup', {android: 'radio group', harmony: undefined})
  .set('scrollbar', {android: 'scrollbar', harmony: 'scrollbar'})
  .set('spinbutton', {android: 'spin button', harmony: undefined})
  .set('switch', {android: 'switch', harmony: 'not ticked, that is (?) button'})
  .set('tab', {android: 'tab', harmony: undefined})
  .set('tablist', {android: 'tablist', harmony: undefined})
  .set('timer', {android: 'timer', harmony: undefined})
  .set('list', {android: 'list', harmony: 'list'})
  .set('toolbar', {android: 'toolbar', harmony: undefined});

const ANNOUNCEMENT_BY_PLATFORM_BY_ROLE = new Map<
  Role,
  Record<'harmony' | 'android', string | undefined>
>()
  .set('alert', {android: 'alert', harmony: undefined})
  .set('alertdialog', {android: undefined, harmony: undefined})
  .set('application', {android: undefined, harmony: undefined})
  .set('article', {android: undefined, harmony: undefined})
  .set('banner', {android: undefined, harmony: undefined})
  .set('button', {android: 'button', harmony: 'button'})
  .set('cell', {android: undefined, harmony: undefined})
  .set('checkbox', {android: 'checkbox', harmony: 'not ticked, checkbox'})
  .set('columnheader', {android: undefined, harmony: undefined})
  .set('combobox', {android: 'combobox', harmony: undefined})
  .set('complementary', {android: undefined, harmony: undefined})
  .set('contentinfo', {android: undefined, harmony: undefined})
  .set('definition', {android: undefined, harmony: undefined})
  .set('dialog', {android: undefined, harmony: undefined})
  .set('directory', {android: undefined, harmony: undefined})
  .set('document', {android: undefined, harmony: undefined})
  .set('feed', {android: undefined, harmony: undefined})
  .set('figure', {android: undefined, harmony: undefined})
  .set('form', {android: undefined, harmony: undefined})
  .set('grid', {android: 'grid', harmony: undefined})
  .set('group', {android: undefined, harmony: undefined})
  .set('heading', {android: '<content> heading', harmony: undefined})
  .set('img', {android: 'unlabeled image', harmony: 'image'})
  .set('link', {android: 'link', harmony: undefined})
  .set('list', {android: 'list', harmony: 'list'})
  .set('listitem', {android: undefined, harmony: undefined})
  .set('log', {android: undefined, harmony: undefined})
  .set('main', {android: undefined, harmony: undefined})
  .set('marquee', {android: undefined, harmony: undefined})
  .set('math', {android: undefined, harmony: undefined})
  .set('menu', {android: 'menu', harmony: undefined})
  .set('menubar', {android: 'menubar', harmony: undefined})
  .set('menuitem', {android: 'menuitem', harmony: undefined})
  .set('meter', {android: undefined, harmony: undefined})
  .set('navigation', {android: undefined, harmony: undefined})
  .set('none', {android: undefined, harmony: undefined})
  .set('note', {android: undefined, harmony: undefined})
  .set('option', {android: undefined, harmony: undefined})
  .set('presentation', {android: undefined, harmony: undefined})
  .set('progressbar', {android: 'progressbar', harmony: 'null %, progressbar'})
  .set('radio', {android: 'radiobutton', harmony: 'option button'})
  .set('radiogroup', {android: 'radiogroup', harmony: undefined})
  .set('region', {android: undefined, harmony: undefined})
  .set('row', {android: undefined, harmony: undefined})
  .set('rowgroup', {android: undefined, harmony: undefined})
  .set('rowheader', {android: undefined, harmony: undefined})
  .set('scrollbar', {android: 'scrollbar', harmony: 'scrollbar'})
  .set('searchbox', {android: 'edit box', harmony: 'edit box'})
  .set('separator', {android: undefined, harmony: undefined})
  .set('slider', {
    android: 'slider',
    harmony:
      'null %, double tap and hold with one finger and swipe left or right to adjust the value',
  })
  .set('spinbutton', {android: 'spinbutton', harmony: undefined})
  .set('status', {android: undefined, harmony: undefined})
  .set('summary', {android: 'summary', harmony: undefined})
  .set('switch', {
    android: '<state> switch',
    harmony: 'not ticked, that is button',
  })
  .set('tab', {android: 'tab', harmony: undefined})
  .set('table', {android: undefined, harmony: undefined})
  .set('tablist', {android: 'tablist', harmony: undefined})
  .set('tabpanel', {android: undefined, harmony: undefined})
  .set('term', {android: undefined, harmony: undefined})
  .set('timer', {android: 'timer', harmony: undefined})
  .set('toolbar', {android: 'toolbar', harmony: undefined})
  .set('tooltip', {android: undefined, harmony: undefined})
  .set('tree', {android: undefined, harmony: undefined})
  .set('treegrid', {android: undefined, harmony: undefined})
  .set('treeitem', {android: undefined, harmony: undefined});

export function ViewAccessibilityTest() {
  return (
    <TestSuite name="Accessibility props">
      <TestSuite name="accessible">
        <TestCase.Example itShould="make screen reader gain focus on the cyan container, but not on the pink container">
          <View
            style={{padding: 32, backgroundColor: 'cyan'}}
            accessible={true}>
            <Text style={{backgroundColor: 'red', padding: 16}}>
              The cyan container is accessible...
            </Text>
          </View>
          <View
            style={{padding: 32, backgroundColor: 'pink'}}
            accessible={false}>
            <Text style={{backgroundColor: 'red', padding: 16}}>
              ...and the pink container is not.
            </Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="accessibilityLabel">
        <TestCase.Example itShould="make the screen reader announce 'I am reading accessibilityLabel content' after focusing the view below in accessibility mode">
          <View
            accessible={true}
            accessibilityLabel="I am reading accessibilityLabel content"
            style={{width: '100%', padding: 16, backgroundColor: 'lightblue'}}>
            <Text importantForAccessibility="no">1. Enable ScreenReader</Text>
            <Text importantForAccessibility="no">2. Focus on this View</Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="aria-label">
        <TestCase.Example itShould="make the screen reader announce 'I am reading aria-label content' after focusing the view below in accessibility mode">
          <View
            accessible={true}
            aria-label="I am reading aria-label content"
            style={{width: '100%', padding: 16, backgroundColor: 'lightblue'}}>
            <Text importantForAccessibility="no">1. Enable ScreenReader</Text>
            <Text importantForAccessibility="no">2. Focus on this View</Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="accessibilityState">
        <TestSuite name="checked">
          <TestCase.Example
            itShould={
              Platform.OS === 'harmony'
                ? 'announce "ticked option tick box"'
                : "announce 'checked option'"
            }>
            <View
              style={{padding: 8}}
              accessible={true}
              accessibilityLabel="option"
              accessibilityRole={Platform.select({harmony: 'checkbox'})}
              accessibilityState={{checked: true}}>
              <Text>1. Enable screen reader</Text>
              <Text>2. Focus on this view</Text>
            </View>
          </TestCase.Example>
        </TestSuite>
        <TestSuite name="disabled">
          <TestCase.Example
            itShould={
              Platform.OS === 'harmony'
                ? "announce 'option button disabled'"
                : "announce 'option disabled'"
            }>
            <View
              style={{padding: 8}}
              accessible={true}
              accessibilityLabel="option"
              accessibilityRole={Platform.select({
                harmony: 'button',
              })}
              accessibilityState={{disabled: true}}>
              <Text>1. Enable screen reader</Text>
              <Text>2. Focus on this view</Text>
            </View>
          </TestCase.Example>
        </TestSuite>
        <TestSuite name="selected">
          <TestCase.Example
            itShould={
              Platform.OS === 'harmony'
                ? "announce 'selected option button'"
                : "announce 'selected option'"
            }>
            <View
              style={{padding: 8}}
              accessible={true}
              accessibilityLabel="option"
              accessibilityRole="button"
              accessibilityState={{selected: true}}>
              <Text>1. Enable screen reader</Text>
              <Text>2. Focus on this view</Text>
            </View>
          </TestCase.Example>
        </TestSuite>
      </TestSuite>
      <TestSuite name="aria-disabled">
        <TestCase.Example
          itShould={
            Platform.OS === 'harmony'
              ? "announce 'option button disabled'"
              : "announce 'option disabled'"
          }>
          <View
            style={{padding: 8}}
            accessible={true}
            aria-disabled={true}
            role={Platform.select({
              harmony: 'button',
            })}
            accessibilityLabel="option">
            <Text>1. Enable screen reader</Text>
            <Text>2. Focus on this view</Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="aria-hidden">
        <TestCase.Example itShould="only announce 'aria hidden false' after gaining focus on a view wrapping both texts">
          <View accessible={true}>
            <View style={{padding: 16}}>
              <Text>aria-hidden: false</Text>
            </View>
            <View
              style={{padding: 16, backgroundColor: '#EEE'}}
              aria-hidden={true}>
              <Text>aria-hidden: true</Text>
            </View>
          </View>
        </TestCase.Example>
        <TestCase.Example itShould="be able to gain focus only on the second item">
          <Text>View: aria-hidden=true</Text>
          <View
            style={{flex: 1, borderColor: 'silver', borderWidth: 5}}
            accessible={true}
            aria-hidden={true}>
            <Text style={{padding: 10}}>This item shouldn't be focusable</Text>
          </View>
          <Text style={{paddingTop: 16}}>View: aria-hidden=false</Text>
          <View
            style={{flex: 1, borderColor: 'silver', borderWidth: 5}}
            accessible={true}
            aria-hidden={false}>
            <Text style={{padding: 10}}>
              This item should be focusable and announced
            </Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="aria-selected">
        <TestCase.Example
          itShould={
            Platform.OS === 'harmony'
              ? "announce 'selected option button'"
              : "announce 'selected option'"
          }>
          <View
            style={{padding: 8}}
            accessible={true}
            accessibilityLabel="option"
            accessibilityRole="button"
            aria-selected={true}>
            <Text>1. Enable screen reader</Text>
            <Text>2. Focus on this view</Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="accessibilityActions">
        <TestSuite name="activate">
          <TestCase.Manual
            itShould="pass when view below is activated by accessibility stuff"
            skip={{android: false, harmony: 'Not supported'}}
            initialState={false}
            arrange={({setState}) => {
              return (
                <View
                  accessible={true}
                  accessibilityLabel="activate action tester"
                  role="button"
                  style={{width: '100%', padding: 16}}
                  accessibilityActions={[{name: 'activate'}]}
                  onAccessibilityAction={e => {
                    if (e.nativeEvent.actionName === 'activate') {
                      setState(true);
                    }
                  }}>
                  <Text importantForAccessibility="no">
                    1. Enable ScreenReader
                  </Text>
                  <Text importantForAccessibility="no">
                    2. Focus on this View
                  </Text>
                  <Text importantForAccessibility="no">
                    3. Activate it by double tapping
                  </Text>
                </View>
              );
            }}
            assert={({expect, state}) => {
              expect(state).to.be.true;
            }}
          />
        </TestSuite>
        <TestSuite name="longpress">
          <TestCase.Manual
            skip={{android: "couldn't make it work", harmony: 'Not supported'}}
            itShould="pass when the user places accessibility focus on the component, then double-taps and holds one finger on the screen"
            initialState={false}
            arrange={({setState}) => {
              return (
                <View
                  accessible={true}
                  role="button"
                  style={{width: '100%', padding: 16}}
                  accessibilityActions={[{name: 'longpress'}]}
                  onAccessibilityAction={e => {
                    if (e.nativeEvent.actionName === 'longpress') {
                      setState(true);
                    }
                  }}>
                  <Text importantForAccessibility="no">
                    1. Enable ScreenReader
                  </Text>
                  <Text importantForAccessibility="no">
                    2. Focus on this View
                  </Text>
                  <Text importantForAccessibility="no">
                    3. Activate it by double tapping
                  </Text>
                </View>
              );
            }}
            assert={({expect, state}) => {
              expect(state).to.be.true;
            }}
          />
        </TestSuite>
        <TestSuite name="copy">
          <TestCase.Manual
            skip={{android: true, harmony: false}}
            itShould="pass after double tapping with three fingers when the TextInput is focused"
            initialState={false}
            arrange={({setState}) => {
              return (
                <TextInput
                  value="content to copy"
                  accessible={true}
                  style={{
                    width: '100%',
                    padding: 16,
                    borderColor: 'gray',
                    borderWidth: 2,
                    fontSize: 12,
                  }}
                  accessibilityActions={[{name: 'copy'}]}
                  onAccessibilityAction={e => {
                    if (e.nativeEvent.actionName === 'copy') {
                      setState(true);
                    }
                  }}
                />
              );
            }}
            assert={({expect, state}) => {
              expect(state).to.be.true;
            }}
          />
        </TestSuite>
      </TestSuite>
      <TestSuite name="aria-valuenow/min/max">
        <TestCase.Manual<number[]>
          initialState={[50]}
          skip={{android: false, harmony: 'Not supported'}}
          itShould="announce values only in range from 0 to 100 and stop incrementing or decrementing values if the current value is equal to aria-valuemax or aria-valuemin"
          arrange={({setState, state}) => {
            return (
              <View
                style={{width: '100%', padding: 16}}
                accessible={true}
                accessibilityRole="adjustable"
                accessibilityActions={[
                  {name: 'increment'},
                  {name: 'decrement'},
                ]}
                onAccessibilityAction={event => {
                  switch (event.nativeEvent.actionName) {
                    case 'increment': {
                      setState(prev => {
                        return [...prev, prev[prev.length - 1] + 25];
                      });
                      break;
                    }
                    case 'decrement': {
                      setState(prev => {
                        return [...prev, prev[prev.length - 1] - 25];
                      });
                      break;
                    }
                  }
                }}
                aria-valuemax={100}
                aria-valuemin={0}
                aria-valuenow={state[state.length - 1]}>
                <Text importantForAccessibility="no">
                  1. Enable ScreenReader
                </Text>
                <Text importantForAccessibility="no">
                  2. Focus on this View
                </Text>
                <Text importantForAccessibility="no">
                  3. Swipe up or down to increment or decrement the value
                </Text>
                <Text importantForAccessibility="no">
                  Current Fake Slider value: {state[state.length - 1]}
                </Text>
              </View>
            );
          }}
          assert={({state}) => {
            return new Promise(resolve => {
              if (state.includes(0) && state.includes(100)) {
                resolve(undefined);
              }
            });
          }}
        />
      </TestSuite>
      <TestSuite name="aria-valuetext">
        <TestCase.Example
          skip={{android: false, harmony: 'Not supported'}}
          itShould="render a view with aria-valuetext accessibility prop">
          <View
            aria-valuetext={'announcing value from aria-valuetext'}
            style={{width: '100%', padding: 16}}>
            <Text importantForAccessibility="no">1. Enable ScreenReader</Text>
            <Text importantForAccessibility="no">2. Focus on this View</Text>
          </View>
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="accessibilityRole">
        <TestCase.Example itShould="announce text specified in the appropriate column">
          <View style={{padding: 16, flexDirection: 'row', width: '100%'}}>
            <Text style={{width: '33%', fontWeight: 'bold'}}>Role</Text>
            <Text style={{width: '33%', fontWeight: 'bold'}}>Android</Text>
            <Text style={{width: '33%', fontWeight: 'bold'}}>Harmony</Text>
          </View>
          {Array.from(
            ANNOUNCEMENT_BY_PLATFORM_BY_ACCESSIBILITY_ROLE.entries(),
          ).map(([role, {android, harmony}]) => {
            return (
              <View
                key={role}
                accessible
                accessibilityRole={role}
                style={{
                  padding: 16,
                  flexDirection: 'row',
                  width: '100%',
                  borderBottomWidth: 1,
                  borderColor: 'silver',
                }}>
                <Text style={{width: '33%'}} importantForAccessibility="no">
                  {role}
                </Text>
                <Text style={{width: '33%'}} importantForAccessibility="no">
                  {android}
                </Text>
                <Text style={{width: '33%'}} importantForAccessibility="no">
                  {harmony}
                </Text>
              </View>
            );
          })}
        </TestCase.Example>
      </TestSuite>
      <TestSuite name="role">
        <TestCase.Example itShould="announce text specified in the appropriate column">
          <View style={{padding: 16, flexDirection: 'row', width: '100%'}}>
            <Text style={{width: '33%', fontWeight: 'bold'}}>Role</Text>
            <Text style={{width: '33%', fontWeight: 'bold'}}>Android</Text>
            <Text style={{width: '33%', fontWeight: 'bold'}}>Harmony</Text>
          </View>
          {Array.from(ANNOUNCEMENT_BY_PLATFORM_BY_ROLE.entries()).map(
            ([role, {android, harmony}]) => {
              return (
                <View
                  key={role}
                  accessible
                  role={role}
                  style={{
                    padding: 16,
                    flexDirection: 'row',
                    width: '100%',
                    borderBottomWidth: 1,
                    borderColor: 'silver',
                  }}>
                  <Text style={{width: '33%'}} importantForAccessibility="no">
                    {role}
                  </Text>
                  <Text style={{width: '33%'}} importantForAccessibility="no">
                    {android}
                  </Text>
                  <Text style={{width: '33%'}} importantForAccessibility="no">
                    {harmony}
                  </Text>
                </View>
              );
            },
          )}
        </TestCase.Example>
      </TestSuite>

      <TestSuite name="aria-labelledby">
        <TestCase.Example
          itShould={`announce: '${Platform.OS === 'android' ? 'edit box for phone number' : 'phone number, edit box, double tap to ...'}' after TextInput gains focus`}>
          <View>
            <Text nativeID="testAriaLabelledBy">Phone Number</Text>
            <TextInput
              style={{backgroundColor: 'lightblue', padding: 8}}
              aria-labelledby={'testAriaLabelledBy'}
              value="focus on me"
            />
          </View>
        </TestCase.Example>

        <TestCase.Example itShould="not mention: 'phone number' after the text input gains focus">
          <View collapsable={false}>
            <View accessibilityLabelledBy={'xxx'}>
              <View collapsable={false} nativeID="testAriaLabelledBy2">
                <Text>
                  Phone Number (aria-labelledby points to a view, not a text)
                </Text>
              </View>
              <TextInput
                aria-labelledby={'testAriaLabelledBy2'}
                style={{backgroundColor: 'lightblue', padding: 8}}
                value="focus on me"
              />
            </View>
          </View>
        </TestCase.Example>
      </TestSuite>

      <TestSuite name="accessibilityLabelledBy">
        <TestCase.Example
          itShould={`announce: '${Platform.OS === 'android' ? 'edit box for phone number in accessibility label' : 'phone number in accessibility label, edit box, double tap to ...'}' after TextInput gains focus`}>
          <View>
            <Text
              accessibilityLabel="phone number in accessibilityLabel"
              nativeID="testAccessibilityLabelledBy">
              Phone Number
            </Text>
            <TextInput
              accessibilityLabelledBy={'testAccessibilityLabelledBy'}
              style={{backgroundColor: 'lightblue', padding: 8}}
              value="focus on me"
            />
          </View>
        </TestCase.Example>
      </TestSuite>
    </TestSuite>
  );
}