'use strict';
import { ReanimatedError } from '../../errors';
import type { TransformsArray, ValueProcessor } from '../../types';
import { isAngle, isNumber, isNumberArray, isPercentage } from '../../utils';

export const ERROR_MESSAGES = {
  invalidTransform: (transform: string) =>
    `Invalid transform property: ${transform}`,
};

function parseValues(valueString: string): (string | number)[] {
  'worklet';
  return valueString.split(',').map((value) => {
    const trimmedValue = value.trim();
    if (['deg', 'rad', '%'].some((unit) => trimmedValue.endsWith(unit))) {
      return trimmedValue;
    }
    const numValue = parseFloat(trimmedValue);
    return isNaN(numValue) ? trimmedValue : numValue;
  });
}

function parseTranslateX(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && (isNumber(values[0]) || isPercentage(values[0]))
    ? [{ translateX: values[0] }]
    : [];
}

function parseTranslateY(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && (isNumber(values[0]) || isPercentage(values[0]))
    ? [{ translateY: values[0] }]
    : [];
}

function parseTranslate(values: (number | string)[]): TransformsArray {
  'worklet';
  if (values.length > 2) {
    return [];
  }
  const result = parseTranslateX([values[0]]).concat(
    parseTranslateY([values[1] ?? values[0]])
  );
  return result.length === 2 ? result : [];
}

function parseScaleX(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && isNumber(values[0])
    ? [{ scaleX: values[0] }]
    : [];
}

function parseScaleY(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && isNumber(values[0])
    ? [{ scaleY: values[0] }]
    : [];
}

function parseScale(values: (number | string)[]): TransformsArray {
  'worklet';
  if (values.length > 2) {
    return [];
  }
  if (values.length === 1) {
    return isNumber(values[0]) ? [{ scale: values[0] }] : [];
  }
  const result = parseScaleX([values[0]]).concat(
    parseScaleY([values[1] ?? values[0]])
  );
  return result.length === 2 ? result : [];
}

function parseRotate(
  key: string,
  values: (string | number)[]
): TransformsArray {
  'worklet';
  return values.length === 1 && (isAngle(values[0]) || values[0] === 0)
    ? ([
        { [key]: values[0] === 0 ? '0deg' : values[0] },
      ] as unknown as TransformsArray)
    : [];
}

function parseSkewX(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && (isAngle(values[0]) || values[0] === 0)
    ? [{ skewX: values[0] === 0 ? '0deg' : values[0] }]
    : [];
}

function parseSkewY(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && (isAngle(values[0]) || values[0] === 0)
    ? [{ skewY: values[0] === 0 ? '0deg' : values[0] }]
    : [];
}

function parseSkew(values: (number | string)[]): TransformsArray {
  'worklet';
  if (values.length > 2) {
    return [];
  }
  const result = parseSkewX([values[0]]).concat(
    parseSkewY([values[1] ?? values[0]])
  );
  return result.length === 2 ? result : [];
}

function parseMatrix(values: (number | string)[]): TransformsArray {
  'worklet';
  let matrixValues: number[] = [];

  if (isNumberArray(values)) {
    if (values.length === 6) {
      // prettier-ignore
      matrixValues = [
        values[0], values[1], 0, 0,
        values[2], values[3], 0, 0,
        0,         0,         1, 0,
        values[4], values[5], 0, 1
      ];
    } else if (values.length === 16) {
      matrixValues = values;
    }
  }

  return matrixValues.length > 0 ? [{ matrix: matrixValues }] : [];
}

function parsePerspective(values: (number | string)[]): TransformsArray {
  'worklet';
  return values.length === 1 && isNumber(values[0])
    ? [{ perspective: values[0] }]
    : [];
}

const parseTransformProperty = (transform: string): TransformsArray => {
  'worklet';
  const [key, valueString] = transform.split(/\(\s*/);
  const values = parseValues(valueString.replace(/\)$/g, ''));

  switch (key) {
    case 'translate':
      return parseTranslate(values);
    case 'translateX':
      return parseTranslateX(values);
    case 'translateY':
      return parseTranslateY(values);
    case 'scale':
      return parseScale(values);
    case 'scaleX':
      return parseScaleX(values);
    case 'scaleY':
      return parseScaleY(values);
    case 'rotate':
    case 'rotateX':
    case 'rotateY':
    case 'rotateZ':
      return parseRotate(key, values);
    case 'skew':
      return parseSkew(values);
    case 'skewX':
      return parseSkewX(values);
    case 'skewY':
      return parseSkewY(values);
    case 'matrix':
      return parseMatrix(values);
    case 'perspective':
      return parsePerspective(values);
    default:
      return [];
  }
};

export const processTransform: ValueProcessor<TransformsArray | string> = (
  value
) => {
  'worklet';
  if (typeof value !== 'string') {
    return value;
  }

  return value
    .split(/\)\s*/)
    .filter(Boolean)
    .flatMap((part) => {
      const parsed = parseTransformProperty(part);

      if (parsed.length === 0) {
        throw new ReanimatedError(ERROR_MESSAGES.invalidTransform(`${part})`));
      }

      return parsed;
    });
};
