#!/usr/bin/env node
// Usage: node parse_ts.js file.ts

const fs = require('fs');
const path = require('path');
const { parse } = require('@typescript-eslint/typescript-estree');

function getLoc(node) {
  if (node.loc) {
    return { start: node.loc.start.line, end: node.loc.end.line };
  }
  return { start: 1, end: 1 };
}

function extractLeadingComment(node, sourceCode) {
  if (!node.range || !sourceCode) return "";
  const startIdx = node.range[0];
  let commentEnd = startIdx;
  // Look backward for comments
  let i = startIdx - 1;
  let commentLines = [];
  let inBlock = false;

  while (i >= 0) {
    const char = sourceCode[i];
    if (char === '\n') break;
    i--;
  }
  const lineStart = i + 1;
  const lineAbove = sourceCode.slice(lineStart, startIdx).trim();

  // Check for // comment on same line before node
  if (lineAbove.startsWith('//')) {
    return lineAbove.substring(2).trim();
  }

  // Look further up for multi-line or JSDoc
  const lines = sourceCode.substring(0, lineStart).split('\n');
  for (let j = lines.length - 1; j >= Math.max(0, lines.length - 5); j--) {
    const line = lines[j].trim();
    if (line.startsWith('//')) {
      commentLines.unshift(line.substring(2).trim());
    } else if (line.endsWith('*/')) {
      inBlock = true;
      commentLines.unshift(line.slice(0, -2).trim());
    } else if (inBlock) {
      if (line.startsWith('/*') || line.startsWith('/**')) {
        commentLines.unshift(line.slice(2).trim());
        break;
      } else {
        commentLines.unshift(line);
      }
    } else if (line === '') {
      if (commentLines.length > 0) continue;
      else break;
    } else {
      break;
    }
  }
  return commentLines.join('\n').trim();
}

function extractDeclarations(ast, sourceCode) {
  const decls = [];

  function visit(node) {
    if (!node || typeof node !== 'object') return;

    if (Array.isArray(node)) {
      node.forEach(n => visit(n));
      return;
    }

    let docComment = extractLeadingComment(node, sourceCode);

    if (node.type === 'FunctionDeclaration' && node.id?.name) {
      const loc = getLoc(node);
      decls.push({
        name: node.id.name,
        type: 'function',
        doc_comment: docComment,
        start_line: loc.start,
        end_line: loc.end
      });
    }
    else if (
      node.type === 'VariableDeclarator' &&
      node.id?.type === 'Identifier' &&
      node.init?.type === 'ArrowFunctionExpression'
    ) {
      const loc = getLoc(node);
      decls.push({
        name: node.id.name,
        type: 'function',
        doc_comment: docComment,
        start_line: loc.start,
        end_line: loc.end
      });
    }
    else if (node.type === 'ClassDeclaration' && node.id?.name) {
      const loc = getLoc(node);
      decls.push({
        name: node.id.name,
        type: 'class',
        doc_comment: docComment,
        start_line: loc.start,
        end_line: loc.end
      });
    }
    else if (node.type === 'TSInterfaceDeclaration' && node.id?.name) {
      const loc = getLoc(node);
      decls.push({
        name: node.id.name,
        type: 'interface',
        doc_comment: docComment,
        start_line: loc.start,
        end_line: loc.end
      });
    }
    else if (node.type === 'TSTypeAliasDeclaration' && node.id?.name) {
      const loc = getLoc(node);
      decls.push({
        name: node.id.name,
        type: 'type',
        doc_comment: docComment,
        start_line: loc.start,
        end_line: loc.end
      });
    }
    else if (
      node.type === 'VariableDeclarator' &&
      node.id?.type === 'Identifier'
    ) {
      const parent = node.parent;
      if (
        parent?.type === 'VariableDeclaration' &&
        ['const', 'let'].includes(parent.kind)
      ) {
        const loc = getLoc(node);
        decls.push({
          name: node.id.name,
          type: 'variable',
          doc_comment: docComment,
          start_line: loc.start,
          end_line: loc.end
        });
      }
    }

    Object.values(node).forEach(visit);
  }

  visit(ast);
  return decls;
}

if (process.argv.length < 3) {
  console.error('Usage: node parse_ts.js <file.ts>');
  process.exit(1);
}

const filePath = path.resolve(process.argv[2]);

try {
  const code = fs.readFileSync(filePath, 'utf8');
  const ast = parse(code, {
    sourceType: 'module',
    loc: true,
    range: true,
    comment: false // we extract manually for simplicity
  });
  const decls = extractDeclarations(ast, code);
  console.log(JSON.stringify(decls, null, 2));
} catch (e) {
  console.error(`ERR: ${e.message}`);
  process.exit(2);
}