import {
  transformsSymbol,
  validTransforms,
  transformsFragmentStrings,
  emptyString,
} from './consts.js';

import {
  isUnd,
  stringStartsWith,
} from './helpers.js';

/**
* @import {
*   DOMTarget,
* } from '../types/index.js'
*/

/**
 * @param  {DOMTarget} target
 * @param  {String} propName
 * @param  {Object} animationInlineStyles
 * @return {String}
 */
export const parseInlineTransforms = (target, propName, animationInlineStyles) => {
  const inlineTransforms = target.style.transform;
  if (inlineTransforms) {
    const cachedTransforms = target[transformsSymbol];
    let pos = 0;
    const len = inlineTransforms.length;
    let fullTranslateValue;
    while (pos < len) {
      // Skip whitespace
      while (pos < len && inlineTransforms.charCodeAt(pos) === 32) pos++;
      if (pos >= len) break;
      // Read function name
      const nameStart = pos;
      while (pos < len && inlineTransforms.charCodeAt(pos) !== 40) pos++;
      if (pos >= len) break;
      const name = inlineTransforms.substring(nameStart, pos);
      // Scan to closing paren, recording top-level comma positions
      let depth = 1;
      const valueStart = pos + 1;
      let c1 = -1, c2 = -1;
      pos++;
      while (pos < len && depth > 0) {
        const c = inlineTransforms.charCodeAt(pos);
        if (c === 40) depth++;
        else if (c === 41) depth--;
        else if (c === 44 && depth === 1) {
          if (c1 === -1) c1 = pos;
          else if (c2 === -1) c2 = pos;
        }
        pos++;
      }
      const valueEnd = pos - 1;
      // Decompose multi-arg functions into individual axis properties
      if (name === 'translate' || name === 'translate3d') {
        if (c1 === -1) {
          cachedTransforms.translateX = inlineTransforms.substring(valueStart, valueEnd).trim();
        } else {
          cachedTransforms.translateX = inlineTransforms.substring(valueStart, c1).trim();
          if (c2 === -1) {
            cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
          } else {
            cachedTransforms.translateY = inlineTransforms.substring(c1 + 1, c2).trim();
            cachedTransforms.translateZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
          }
        }
        fullTranslateValue = inlineTransforms.substring(valueStart, valueEnd);
      } else if (name === 'scale' || name === 'scale3d') {
        if (c1 === -1) {
          cachedTransforms.scale = inlineTransforms.substring(valueStart, valueEnd).trim();
        } else {
          cachedTransforms.scaleX = inlineTransforms.substring(valueStart, c1).trim();
          if (c2 === -1) {
            cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, valueEnd).trim();
          } else {
            cachedTransforms.scaleY = inlineTransforms.substring(c1 + 1, c2).trim();
            cachedTransforms.scaleZ = inlineTransforms.substring(c2 + 1, valueEnd).trim();
          }
        }
      } else {
        cachedTransforms[name] = inlineTransforms.substring(valueStart, valueEnd);
      }
    }
    // Resolve the requested property from the cache
    if (propName === 'translate3d' && fullTranslateValue) {
      if (animationInlineStyles) animationInlineStyles[propName] = fullTranslateValue;
      return fullTranslateValue;
    }
    const cached = cachedTransforms[propName];
    if (!isUnd(cached)) {
      if (animationInlineStyles) animationInlineStyles[propName] = cached;
      return cached;
    }
  }
  return propName === 'translate3d' ? '0px, 0px, 0px' :
    propName === 'rotate3d' ? '0, 0, 0, 0deg' :
    stringStartsWith(propName, 'scale') ? '1' :
    stringStartsWith(propName, 'rotate') || stringStartsWith(propName, 'skew') ? '0deg' : '0px';
}

/**
 * Builds a CSS transform string from the target's cached transform properties.
 * Iterates validTransforms in order (perspective > translate > rotate > scale > skew > matrix).
 * When adjacent axis properties are all present, emits a shorter shorthand (translateX + translateY -> translate(x, y))
 * The index is advanced past consumed properties so they are not emitted twice.
 * Properties without a grouping partner (e.g. translateY alone, scaleZ alone) emit individually.
 *
 * @param  {Record<String, String>} props
 * @return {String}
 */
export const buildTransformString = (props) => {
  let str = emptyString;
  for (let i = 0, l = validTransforms.length; i < l; i++) {
    const key = validTransforms[i];
    const val = props[key];
    if (val !== undefined) {
      // Group translateX with adjacent translateY / translateZ
      if (key === 'translateX') {
        const next = props.translateY;
        if (next !== undefined) {
          const next2 = props.translateZ;
          if (next2 !== undefined) {
            str += `translate3d(${val},${next},${next2}) `;
            i += 2;
          } else {
            str += `translate(${val},${next}) `;
            i += 1;
          }
          continue;
        }
      }
      // Group scaleX with adjacent scaleY / scaleZ (only when standalone scale is absent)
      if (key === 'scaleX' && props.scale === undefined) {
        const next = props.scaleY;
        if (next !== undefined) {
          const next2 = props.scaleZ;
          if (next2 !== undefined) {
            str += `scale3d(${val},${next},${next2}) `;
            i += 2;
          } else {
            str += `scale(${val},${next}) `;
            i += 1;
          }
          continue;
        }
      }
      // All other properties: emit individually using pre-built fragment string
      str += `${transformsFragmentStrings[key]}${val}) `;
    }
    // Preserve non-animatable rotate3d in correct position (after rotateZ, before scale)
    if (key === 'rotateZ') {
      if (props.rotate3d !== undefined) str += `rotate3d(${props.rotate3d}) `;
    }
  }
  // Preserve non-animatable matrix/matrix3d from inline styles
  if (props.matrix !== undefined) str += `matrix(${props.matrix}) `;
  if (props.matrix3d !== undefined) str += `matrix3d(${props.matrix3d}) `;
  return str;
}
