#!/usr/bin/env bash
# generate_cypress_dts.sh
#
# Usage:
#   ./scripts/generate_cypress_dts.sh <path-to-atk_commands.js> [output.d.ts]
#
# Description:
#   Parses JSDoc-style comments that immediately precede
#   `Cypress.Commands.add('commandName', ...)` in a Cypress commands file and
#   generates a TypeScript declaration file with a Cypress.Chainable interface.
#
# Notes:
# - Implemented using an embedded Node.js script for reliable parsing.
# - Maps common JSDoc types to TypeScript and wraps returns as Chainable<T>.
# - If no @returns/@return is found, defaults to Chainable<any>.
# - If no @param is found, emits the command with no parameters.
#
set -euo pipefail

if [[ $# -lt 1 ]]; then
  echo "Usage: $0 <path-to-atk_commands.js> [index.d.ts]" >&2
  exit 1
fi

SRC_FILE="$1"
if [[ ! -f "$SRC_FILE" ]]; then
  echo "Source file not found: $SRC_FILE" >&2
  exit 1
fi

# Default output next to source as index.d.ts if not provided
if [[ $# -ge 2 ]]; then
  OUT_FILE="$2"
else
  SRC_DIR=$(dirname "$SRC_FILE")
  OUT_FILE="$SRC_DIR/index.d.ts"
fi

# Run Node.js parser
node - "$SRC_FILE" "$OUT_FILE" <<'NODE'
const fs = require('fs');
const path = require('path');

function readFile(p) { return fs.readFileSync(p, 'utf8'); }

function parse(js) {
  const entries = [];
  const addRe = /Cypress\.[Cc]ommands\.add\(\s*['"`]([^'"`]+)['"`][,\s]+\(([^\)]*)\)/g;
  let m;
  while ((m = addRe.exec(js))) {
    const name = m[1];
    const originalSignature = m[2];
    // Remove defaults
    const signature = originalSignature.split(',')
      .map((param) => param.split('=')[0])
      .join(',');
    const addIndex = m.index;
    // Walk backwards from the start of Commands.add to capture immediately preceding JSDoc
    let i = addIndex - 1;
    // Skip whitespace
    while (i >= 0 && /\s/.test(js[i])) i--;
    // Expect to be at the end of a JSDoc: */
    if (!(i >= 1 && js[i] === '/' && js[i-1] === '*')) {
      // No immediate JSDoc block
      continue;
    }
    // Find the start of that JSDoc: /**
    let endPos = i + 1; // position after '/'
    // Move back to find '/**'
    let startPos = -1;
    for (let j = i - 1; j >= 2; j--) {
      if (js[j-2] === '/' && js[j-1] === '*' && js[j] === '*') {
        startPos = j - 2;
        break;
      }
    }
    if (startPos === -1) {
      continue;
    }
    const doc = js.slice(startPos + 3, endPos - 2); // inside of /** ... */
    entries.push({ name, signature, doc });
  }
  return entries;
}

function generate(entries) {
  const lines = [];
  lines.push('declare namespace Cypress {');
  lines.push('  interface Chainable<Subject = any> {');
  for (const e of entries) {
    // Re-emit the original doc block verbatim
    lines.push('    /**');
    // Ensure the captured doc already includes leading * lines; indent each line to align
    const docText = e.doc.replace(/^[\r\n]+|[\r\n]+$/g, '');
    for (const l of docText.split('\n')) {
      // Normalize to a single leading asterisk and a space, then indent 4 spaces
      const normalized = l.replace(/^\s*\*?\s?/, ' * ');
      lines.push('    ' + normalized.trimEnd());
    }
    lines.push('    */');
    // Emit a generic originalSignature to avoid altering types
    lines.push(`    ${e.name}(${e.signature}): Chainable<any>;`);
    lines.push('');
  }
  lines.push('  }');
  lines.push('}');
  return lines.join('\n');
}

const src = process.argv[2];
const out = process.argv[3];
const js = readFile(src);
const entries = parse(js);
const dts = generate(entries);
fs.mkdirSync(path.dirname(out), { recursive: true });
fs.writeFileSync(out, dts, 'utf8');
console.error(`Generated ${out} with ${entries.length} entries.`);
NODE

chmod +x "$0"

echo "Wrote TypeScript declarations to: $OUT_FILE"