import fs from 'node:fs/promises';
import parse, { type DOMNode } from 'html-dom-parser';
import type { ChildNode } from 'domhandler';

const JSD_STRING = /\(\s*(<.*)>\s*\)/gs;

export async function parseJSDFile(filename: string) {
  const content = (await fs.readFile(filename)).toString();

  const matches = JSD_STRING.exec(content);
  if (matches) {
    let html = matches[1] + '>';
    const root = parse(html);
    const translated = translate(root[0]);
    const str = content.replace(matches[1] + '>', translated);
    await fs.writeFile(filename.replace('.tsd', '.ts'), str);
  }
  return true;
}

interface state {
  inInterpolation?: boolean;
  children?: string[][];
  parent?: Text[];
}

function translate(root: DOMNode | ChildNode | null, state: state = {}): string | null {
  if (!root || typeof root !== 'object') return null;

  let children = [];
  if ('children' in root && Array.isArray(root.children) && root.children.length > 0) {
    for (const child of root.children) {
      const translated = translate(child, state);
      if (translated) {
        if (state.inInterpolation && state.parent[state.children.length - 1] === child) {
          state.children[state.children.length - 1].push(translated);
        } else {
          children.push(translated);
        }
      }
    }
  }

  if ('nodeType' in root && root.nodeType === 3) {
    if (root.data.trim() === '') return null;
    return parseText(root.data.trim(), state, root);
  }

  if ('name' in root && root.name) {
    let tagName = root.name || 'unknown';
    let attrs = 'attribs' in root ? root.attribs : {};
    return `StarKitten.createElement("${tagName}", ${JSON.stringify(attrs)}${children.length > 0 ? ', ' + children.join(', ') : ''})`;
  }
}

const JSD_INTERPOLATION = /\{(.+)\}/gs;
const JSD_START_EXP_INTERPOLATION = /\{(.+)\(/gs;
const JSD_END_EXP_INTERPOLATION = /\)(.+)\}/gs;

function parseText(text: string, state: state = {}, parent: Text = {}): string {
  let interpolations = text.match(JSD_INTERPOLATION);
  if (!interpolations) {
    if (text.match(JSD_START_EXP_INTERPOLATION)) {
      state.inInterpolation = true;
      state.children = state.children || [[]];
      state.parent = state.parent || [];
      state.parent.push(parent);
      return text.substring(1, text.length - 1);
    } else if (text.match(JSD_END_EXP_INTERPOLATION)) {
      const combined = state.children?.[state.children.length - 1].join(' ');
      state.children?.[state.children.length - 1].splice(0);
      state.children?.pop();
      state.parent?.pop();
      if (state.children.length === 0) {
        state.inInterpolation = false;
        return combined + ' ' + text.substring(1, text.length - 1);
      }
    }
    return `"${text}"`;
  } else {
    text = replaceInterpolations(text);
    return `"${text}"`;
  }
}

function replaceInterpolations(text: string, isOnJSON: boolean = false) {
  let interpolations = null;

  while ((interpolations = JSD_INTERPOLATION.exec(text))) {
    if (isOnJSON) {
      text = text.replace(`"{${interpolations[1]}}"`, interpolations[1]);
    } else {
      text = text.replace(`{${interpolations[1]}}`, `"+ ${interpolations[1]} +"`);
    }
  }
  return text;
}
