import { parseInlineStyle } from '../css/parse.mjs';
import { tokensToString } from '../css/parser/export.mjs';
import { getTokens } from '../css/parser/tokens.mjs';
import { tokensTree } from '../css/parser/tree.mjs';
import { parseSVG } from './parse.mjs';
import '../css/parser/error.mjs';
import '../css/parser/strings.mjs';
import '../css/parser/text.mjs';

function assertNotOldCode(value) {
  if (value instanceof Promise) {
    throw new Error("parseSVGStyle does not support async callbacks");
  }
}
function parseSVGStyle(svg, callback) {
  parseSVG(svg, (item) => {
    const tagName = item.tagName;
    const $element = item.$element;
    function parseStyleItem() {
      const content = $element.text();
      if (typeof content !== "string") {
        $element.remove();
        return;
      }
      const tokens = getTokens(content);
      if (!(tokens instanceof Array)) {
        throw new Error("Error parsing style");
      }
      let changed2 = false;
      const selectorStart = [];
      let newTokens = [];
      while (tokens.length) {
        const token = tokens.shift();
        if (!token) {
          break;
        }
        switch (token.type) {
          case "selector":
            selectorStart.push(newTokens.length);
            newTokens.push(token);
            break;
          case "close":
            selectorStart.pop();
            newTokens.push(token);
            break;
          case "at-rule": {
            selectorStart.push(newTokens.length);
            const prop = token.rule;
            const value = token.value;
            const isAnimation = prop === "keyframes" || prop.slice(0, 1) === "-" && prop.split("-").pop() === "keyframes";
            const childTokens = [];
            const animationRules = /* @__PURE__ */ Object.create(null);
            let depth = 1;
            let index = 0;
            let isFrom = false;
            while (depth > 0) {
              const childToken = tokens[index];
              index++;
              if (!childToken) {
                throw new Error(
                  "Something went wrong parsing CSS"
                );
              }
              childTokens.push(childToken);
              switch (childToken.type) {
                case "close": {
                  depth--;
                  isFrom = false;
                  break;
                }
                case "selector": {
                  depth++;
                  if (isAnimation) {
                    const rule = childToken.code;
                    if (rule === "from" || rule === "0%") {
                      isFrom = true;
                    }
                  }
                  break;
                }
                case "at-rule": {
                  depth++;
                  if (isAnimation) {
                    throw new Error(
                      "Nested at-rule in keyframes ???"
                    );
                  }
                  break;
                }
                case "rule": {
                  if (isAnimation && isFrom) {
                    animationRules[childToken.prop] = childToken.value;
                  }
                  break;
                }
              }
            }
            const skipCount = childTokens.length;
            const result = callback(
              isAnimation ? {
                type: "keyframes",
                prop,
                value,
                token,
                childTokens,
                from: animationRules,
                prevTokens: newTokens,
                nextTokens: tokens.slice(0)
              } : {
                type: "at-rule",
                prop,
                value,
                token,
                childTokens,
                prevTokens: newTokens,
                nextTokens: tokens.slice(0)
              }
            );
            if (result !== void 0) {
              assertNotOldCode(result);
              if (isAnimation) {
                if (result !== value) {
                  changed2 = true;
                  token.value = result;
                }
                newTokens.push(token);
                for (let i = 0; i < skipCount; i++) {
                  tokens.shift();
                }
                newTokens = newTokens.concat(childTokens);
              } else {
                if (result !== value) {
                  throw new Error(
                    "Changing value for at-rule is not supported"
                  );
                }
                newTokens.push(token);
              }
            } else {
              changed2 = true;
              for (let i = 0; i < skipCount; i++) {
                tokens.shift();
              }
            }
            break;
          }
          case "rule": {
            const value = token.value;
            const selectorTokens = selectorStart.map((index) => newTokens[index]).filter((item2) => item2 !== null);
            const result = callback({
              type: "global",
              prop: token.prop,
              value,
              token,
              selectorTokens,
              selectors: selectorTokens.reduce(
                (prev, current) => {
                  switch (current.type) {
                    case "selector": {
                      return prev.concat(
                        current.selectors
                      );
                    }
                  }
                  return prev;
                },
                []
              ),
              prevTokens: newTokens,
              nextTokens: tokens.slice(0)
            });
            if (result !== void 0) {
              assertNotOldCode(result);
              if (result !== value) {
                changed2 = true;
                token.value = result;
              }
              newTokens.push(token);
            } else {
              changed2 = true;
            }
            break;
          }
        }
      }
      if (changed2) {
        const tree = tokensTree(
          newTokens.filter((token) => token !== null)
        );
        if (!tree.length) {
          $element.remove();
        } else {
          const newContent = tokensToString(tree);
          item.$element.text("\n" + newContent);
        }
      }
    }
    if (tagName === "style") {
      parseStyleItem();
      return;
    }
    const attribs = item.element.attribs;
    if (attribs.style === void 0) {
      return;
    }
    const parsedStyle = parseInlineStyle(attribs.style);
    if (parsedStyle === null) {
      $element.removeAttr("style");
      return;
    }
    let changed = false;
    for (const prop in parsedStyle) {
      const value = parsedStyle[prop];
      const result = callback({
        type: "inline",
        prop,
        value,
        item
      });
      assertNotOldCode(result);
      if (result !== value) {
        changed = true;
        if (result === void 0) {
          delete parsedStyle[prop];
        } else {
          parsedStyle[prop] = result;
        }
      }
    }
    if (changed) {
      const newStyle = Object.keys(parsedStyle).map((key) => key + ":" + parsedStyle[key] + ";").join("");
      if (!newStyle.length) {
        $element.removeAttr("style");
      } else {
        $element.attr("style", newStyle);
      }
    }
  });
}

export { parseSVGStyle };
