export const getMatchAt = (str, regexp, idx = 1, defaultValue = '') => {
    const matches = ('' + str).match(regexp);
    return (matches && matches[idx]) || defaultValue;
};

export const embeddedSvgImageToInlineSvgContent = svgStr =>
    svgStr.replace(/<image([^>]+)xlink:href="data:image\/svg\+xml;base64,([^"]+)"[^>]*>/g, function(
        _,
        attributesStr,
        base64Str
    ) {
        const x = getMatchAt(attributesStr, /x="([^"]+)"/, 1, 0);
        const y = getMatchAt(attributesStr, /y="([^"]+)"/, 1, 0);
        const transform = getMatchAt(attributesStr, /transform="([^"]+)"/, 1, '');
        const svgContent = atob(base64Str).replace(/(<\?xml[^>]+>)|(<!--[^>]+-->)/gi, '');
        return `<g transform="${transform} translate(${x},${y})">${svgContent}</g>`;
    });

export const alterObjectValues = (obj, fn) => {
    Object.keys(obj).forEach(key => {
        obj[key] = fn(obj[key], key);
    });
    return obj;
};

export const alterArrayValues = (arr, fn) => {
    arr.forEach((value, key) => {
        arr[key] = fn(value, key);
    });
    return arr;
};

export const prepareDesignDataSnapshots = svgArrMap =>
    alterObjectValues(svgArrMap, arr => alterArrayValues(arr, embeddedSvgImageToInlineSvgContent));

export const preparePrintDesignData = svgMap => alterObjectValues(svgMap, embeddedSvgImageToInlineSvgContent);

/**
 * Calculates the bounding viewBox for an SVG string.
 * @param {string} svgString - The raw SVG string from the API.
 * @returns {string} The calculated viewBox (e.g., "0 0 100 100").
 */
export const getMissingViewBox = (svgString, fallbackSize) => {
    const padding = 2;

    // 1. Parse the SVG string into a DOM element
    const parser = new DOMParser();
    const doc = parser.parseFromString(svgString, 'image/svg+xml');
    const svgElement = doc.querySelector('svg');

    if (!svgElement) {
        throw new Error('Invalid SVG string provided.');
    }

    // 2. Prepare the SVG to be rendered invisibly
    if (svgElement.style) {
        svgElement.style.position = 'absolute';
        svgElement.style.visibility = 'hidden';
        svgElement.style.width = 'auto';
        svgElement.style.height = 'auto';
    } else {
        svgElement.setAttribute(
            'style',
            'position: absolute; visibility: hidden; width: auto; height: auto;'
        );
    }

    // 3. Wrap all children in a <g> tag.
    // Calling getBBox() directly on the <svg> root can be unreliable across
    // different browsers. A wrapper group guarantees we measure the actual content.
    const wrapperGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
    while (svgElement.firstChild) {
        wrapperGroup.appendChild(svgElement.firstChild);
    }
    svgElement.appendChild(wrapperGroup);

    // 4. Attach to the DOM so the browser can calculate layout dimensions
    document.body.appendChild(svgElement);

    // 5. Calculate the bounding box
    let viewBox = `0 0 ${fallbackSize.width} ${fallbackSize.height}`;

    if (!wrapperGroup.getBBox) {
        return viewBox;
    }

    try {
        const bbox = wrapperGroup.getBBox();

        const x = bbox.x - padding;
        const y = bbox.y - padding;
        const width = bbox.width + padding * 2;
        const height = bbox.height + padding * 2;
        // Construct the viewBox string: min-x, min-y, width, height
        viewBox = `${x} ${y} ${width} ${height}`;
    } finally {
        // 6. Clean up by removing the hidden SVG from the DOM
        document.body.removeChild(svgElement);
    }

    return viewBox;
};
