/**
 * 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 {useContext, useEffect, useState, type ReactElement} from 'react';
import {TestCaseState} from '../core';
import {TestCaseContext} from './TestingContext';
import {AssertionError, expect as expect_} from 'chai';
import {TestCaseStateTemplate} from './TestCaseStateTemplate';
import {PALETTE} from './palette';
import {StyleSheet, Text, View} from 'react-native';

export type SmartAutomatedTestCaseProps<TState> = {
  initialState: TState;
  itShould: string;
  skip?: boolean | string;
  modal?: boolean;
  arrange: (ctx: {
    state: TState;
    setState: React.Dispatch<React.SetStateAction<TState>>;
    reset: () => void;
    done: () => void;
  }) => ReactElement;
  act: (ctx: {
    state: TState;
    done: () => void;
    setState: React.Dispatch<React.SetStateAction<TState>>;
  }) => void;
  assert: (utils: {
    expect: typeof expect_;
    state: TState;
  }) => Promise<void> | void;
};

export function AutomatedTestCase<TState>({
  initialState,
  itShould,
  skip,
  modal,
  arrange,
  act,
  assert,
}: SmartAutomatedTestCaseProps<TState>) {
  const [value, setValue] = useState(initialState);
  const [isDone, setIsDone] = useState(false);
  const [result, setResult] = useState<TestCaseState>(
    skip
      ? {
          status: 'skipped',
          message: typeof skip === 'string' ? skip : undefined,
        }
      : {status: 'waitingForTester'},
  );
  const {reportTestCaseResult} = useContext(TestCaseContext)!;

  const handleTestCaseDone = async () => {
    try {
      await assert({expect: expect_, state: value});
      setResult({status: 'pass'});
      reportTestCaseResult('pass');
    } catch (err) {
      if (err instanceof AssertionError) {
        setResult({status: 'fail', message: err.message});
        reportTestCaseResult('fail');
      } else if (err instanceof Error) {
        setResult({status: 'broken', message: err.message});
        reportTestCaseResult('broken');
      } else {
        setResult({status: 'broken', message: ''});
        reportTestCaseResult('broken');
      }
    }
  };

  useEffect(() => {
    if (skip) {
      reportTestCaseResult('skipped');
      return;
    }

    if (isDone) {
      handleTestCaseDone();
    } else {
      reportTestCaseResult('waitingForTester');
      setResult({status: 'running'});
      act({done: () => setIsDone(true), state: value, setState: setValue});
    }
  }, [isDone]);

  const renderTestContent = () => (
    <View style={styles.testContainer}>
      {arrange({
        state: value,
        setState: setValue,
        reset: () => {
          if (skip) {
            setResult({
              status: 'skipped',
              message: typeof skip === 'string' ? skip : undefined,
            });
          } else {
            setResult({status: 'waitingForTester'});
          }
          setValue(initialState);
          reportTestCaseResult('waitingForTester');
        },
        done: () => setIsDone(true),
      })}
    </View>
  );

  const renderTestDetails = () => {
    if (
      result.status !== 'broken' &&
      result.status !== 'fail' &&
      result.status !== 'skipped'
    ) {
      return null;
    }
    return (
      <Text
        style={[
          styles.textDetails,
          {color: result.status === 'skipped' ? PALETTE.yellow : PALETTE.red},
        ]}>
        {result.message}
      </Text>
    );
  };

  return (
    <TestCaseStateTemplate
      name={itShould}
      renderStatusLabel={() => {
        const labelInfo = STATUS_LABEL_DATA_BY_TEST_CASE_STATUS[result.status];
        return (
          <Text style={[styles.testCaseStatus, {color: labelInfo.color}]}>
            {labelInfo.label}
          </Text>
        );
      }}
      renderModal={
        modal
          ? () => (
              <>
                {renderTestContent()}
                {renderTestDetails()}
              </>
            )
          : undefined
      }
      renderDetails={
        modal
          ? renderTestDetails
          : () => (
              <>
                {renderTestContent()}
                {renderTestDetails()}
              </>
            )
      }
    />
  );
}

const STATUS_LABEL_DATA_BY_TEST_CASE_STATUS: Record<
  TestCaseState['status'],
  {label: string; color: string}
> = {
  broken: {label: 'BROKEN', color: PALETTE.red},
  fail: {label: 'FAIL', color: PALETTE.red},
  pass: {label: 'PASS', color: PALETTE.green},
  skipped: {label: 'SKIPPED', color: PALETTE.yellow},
  running: {label: 'RUNNING', color: PALETTE.gray},
  waitingForTester: {label: 'WAITING', color: PALETTE.blue},
};

const styles = StyleSheet.create({
  testContainer: {
    borderWidth: 4,
    borderColor: '#666',
    backgroundColor: 'white',
    marginBottom: 8,
  },
  testCaseStatus: {
    width: 72,
    height: '100%',
    fontSize: 12,
    fontWeight: 'bold',
    textAlign: 'right',
    color: '#AAA',
  },
  textDetails: {
    width: '100%',
    padding: 2,
    fontSize: 10,
    color: 'white',
    marginBottom: 16,
    backgroundColor: 'rgba(0,0,0,0.1)',
  },
});