// src/actions/dead/collectSymbols.ts
import fs from "node:fs";
import path from "node:path";
import ts from "typescript";
function getLineCol(sourceFile, pos) {
  const lc = sourceFile.getLineAndCharacterOfPosition(pos);
  return { column: lc.character + 1, line: lc.line + 1 };
}
function hasExportModifier(node) {
  if (!ts.canHaveModifiers(node)) return false;
  const mods = ts.getModifiers(node);
  if (!mods) return false;
  return mods.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
}
function hasDefaultModifier(node) {
  if (!ts.canHaveModifiers(node)) return false;
  const mods = ts.getModifiers(node);
  if (!mods) return false;
  return mods.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword);
}
function pushDeclaration(declarations, sourceFile, name, nameNode, exported, exportedAs) {
  const { line, column } = getLineCol(sourceFile, nameNode.getStart(sourceFile));
  declarations.push({
    column,
    exported,
    exportedAs,
    line,
    name
  });
}
function tryNamedDeclaration(node) {
  if (ts.isFunctionDeclaration(node) && node.name) return { name: node.name };
  if (ts.isClassDeclaration(node) && node.name) return { name: node.name };
  if (ts.isInterfaceDeclaration(node)) return { name: node.name };
  if (ts.isTypeAliasDeclaration(node)) return { name: node.name };
  if (ts.isEnumDeclaration(node)) return { name: node.name };
  if (ts.isModuleDeclaration(node) && node.name && ts.isIdentifier(node.name)) return { name: node.name };
  return void 0;
}
function collectBindingNames(node, push) {
  if (ts.isIdentifier(node)) {
    push(node.text);
    return;
  }
  if (ts.isObjectBindingPattern(node) || ts.isArrayBindingPattern(node)) {
    for (const el of node.elements) {
      if (ts.isBindingElement(el)) collectBindingNames(el.name, push);
    }
  }
}
function handleVariableStatement(node, declarations, sourceFile) {
  const exported = hasExportModifier(node);
  for (const decl of node.declarationList.declarations) {
    collectBindingNames(decl.name, (name) => {
      pushDeclaration(declarations, sourceFile, name, decl.name, exported, name);
    });
  }
}
function handleExportAssignment(node, declarations, sourceFile) {
  const pos = getLineCol(sourceFile, node.getStart(sourceFile));
  if (ts.isIdentifier(node.expression)) {
    const name = node.expression.text;
    const existing = declarations.find((d) => d.name === name);
    if (existing) {
      existing.exported = true;
      existing.exportedAs = "default";
    }
  }
  return pos;
}
function handleNamedExportClauseWithModule(clause, moduleSpec, reExportsFromRelative) {
  for (const element of clause.elements) {
    const exportedName = element.name.text;
    const importedName = element.propertyName?.text ?? exportedName;
    reExportsFromRelative.push({
      exportedAs: exportedName,
      imported: importedName,
      specifier: moduleSpec
    });
  }
}
function handleNamedExportClauseLocal(clause, declarations) {
  for (const element of clause.elements) {
    const exportedName = element.name.text;
    const importedName = element.propertyName?.text ?? exportedName;
    const existing = declarations.find((d) => d.name === importedName);
    if (existing) {
      existing.exported = true;
      existing.exportedAs = exportedName;
    }
  }
}
function handleExportDeclaration(node, declarations, reExportsFromRelative, reExportAllFromRelative) {
  const moduleSpec = node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : void 0;
  const isRelative = moduleSpec?.startsWith(".") ?? false;
  if (node.exportClause && ts.isNamedExports(node.exportClause)) {
    if (moduleSpec && isRelative) {
      handleNamedExportClauseWithModule(node.exportClause, moduleSpec, reExportsFromRelative);
    } else if (!moduleSpec) {
      handleNamedExportClauseLocal(node.exportClause, declarations);
    }
    return;
  }
  if (!node.exportClause && moduleSpec && isRelative) {
    reExportAllFromRelative.add(moduleSpec);
  }
}
function visitTopLevel(node, ctx) {
  const named = tryNamedDeclaration(node);
  if (named?.name) {
    pushDeclaration(
      ctx.declarations,
      ctx.sourceFile,
      named.name.text,
      named.name,
      hasExportModifier(node),
      hasDefaultModifier(node) ? "default" : named.name.text
    );
    return;
  }
  if (ts.isVariableStatement(node)) {
    handleVariableStatement(node, ctx.declarations, ctx.sourceFile);
    return;
  }
  if (ts.isExportAssignment(node)) {
    ctx.setDefaultExport(handleExportAssignment(node, ctx.declarations, ctx.sourceFile));
    return;
  }
  if (ts.isExportDeclaration(node)) {
    handleExportDeclaration(node, ctx.declarations, ctx.reExportsFromRelative, ctx.reExportAllFromRelative);
  }
}
function handleNamedImports(spec, clause, ctx) {
  for (const element of clause.elements) {
    const importedName = element.propertyName?.text ?? element.name.text;
    const alias = element.name.text;
    ctx.importBindingNames.add(alias);
    ctx.namedImportsFromRelative.push({
      alias,
      imported: importedName,
      specifier: spec
    });
  }
}
function visitImports(node, ctx) {
  if (!ts.isImportDeclaration(node)) return;
  if (!ts.isStringLiteral(node.moduleSpecifier)) return;
  const spec = node.moduleSpecifier.text;
  if (!spec.startsWith(".")) return;
  const clause = node.importClause;
  if (!clause) {
    ctx.sideEffectFromRelative.add(spec);
    return;
  }
  if (clause.name) {
    const alias = clause.name.text;
    ctx.importBindingNames.add(alias);
    ctx.namedImportsFromRelative.push({
      alias,
      imported: "default",
      specifier: spec
    });
  }
  if (clause.namedBindings) {
    if (ts.isNamespaceImport(clause.namedBindings)) {
      ctx.importBindingNames.add(clause.namedBindings.name.text);
      ctx.namespaceImportFromRelative.add(spec);
    } else if (ts.isNamedImports(clause.namedBindings)) {
      handleNamedImports(spec, clause.namedBindings, ctx);
    }
  }
}
function isTopLevelDeclarationName(node, parent) {
  if (ts.isFunctionDeclaration(parent) && parent.name === node) return true;
  if (ts.isClassDeclaration(parent) && parent.name === node) return true;
  if (ts.isInterfaceDeclaration(parent) && parent.name === node) return true;
  if (ts.isTypeAliasDeclaration(parent) && parent.name === node) return true;
  if (ts.isEnumDeclaration(parent) && parent.name === node) return true;
  if (ts.isModuleDeclaration(parent) && parent.name === node) return true;
  return false;
}
function isLocalBindingName(node, parent) {
  if (ts.isVariableDeclaration(parent) && parent.name === node) return true;
  if (ts.isParameter(parent) && parent.name === node) return true;
  if (ts.isBindingElement(parent) && parent.name === node) return true;
  return false;
}
function isMemberName(node, parent) {
  if (ts.isPropertyAssignment(parent) && parent.name === node) return true;
  if (ts.isPropertyDeclaration(parent) && parent.name === node) return true;
  if (ts.isMethodDeclaration(parent) && parent.name === node) return true;
  if (ts.isPropertySignature(parent) && parent.name === node) return true;
  if (ts.isMethodSignature(parent) && parent.name === node) return true;
  return false;
}
function isImportExportBindingName(node, parent) {
  if (ts.isImportSpecifier(parent) && (parent.name === node || parent.propertyName === node)) return true;
  if (ts.isImportClause(parent) && parent.name === node) return true;
  if (ts.isNamespaceImport(parent) && parent.name === node) return true;
  if (ts.isExportSpecifier(parent) && (parent.name === node || parent.propertyName === node)) return true;
  return false;
}
function isQualifiedMemberAccess(node, parent) {
  if (ts.isQualifiedName(parent) && parent.right === node) return true;
  if (ts.isPropertyAccessExpression(parent) && parent.name === node) return true;
  return false;
}
function isDeclarationOrBindingPosition(node, parent) {
  return isTopLevelDeclarationName(node, parent) || isLocalBindingName(node, parent) || isMemberName(node, parent) || isImportExportBindingName(node, parent) || isQualifiedMemberAccess(node, parent);
}
function visitForReferences(node, identifierRefs) {
  if (ts.isIdentifier(node)) {
    const parent = node.parent;
    if (!parent || !isDeclarationOrBindingPosition(node, parent)) {
      identifierRefs.add(node.text);
    }
  }
  ts.forEachChild(node, (child) => visitForReferences(child, identifierRefs));
}
function collectSymbolsFromFile(filePath) {
  const sourceText = fs.readFileSync(filePath, "utf8");
  const isJsLike = /\.(?:js|mjs|cjs)$/i.test(filePath);
  const sourceFile = ts.createSourceFile(
    path.basename(filePath),
    sourceText,
    ts.ScriptTarget.Latest,
    true,
    isJsLike ? ts.ScriptKind.JS : void 0
  );
  const declarations = [];
  const identifierRefs = /* @__PURE__ */ new Set();
  const namedImportsFromRelative = [];
  const namespaceImportFromRelative = /* @__PURE__ */ new Set();
  const sideEffectFromRelative = /* @__PURE__ */ new Set();
  const reExportsFromRelative = [];
  const reExportAllFromRelative = /* @__PURE__ */ new Set();
  let defaultExport;
  const importBindingNames = /* @__PURE__ */ new Set();
  const topCtx = {
    declarations,
    reExportAllFromRelative,
    reExportsFromRelative,
    setDefaultExport: (pos) => {
      defaultExport = pos;
    },
    sourceFile
  };
  const importCtx = {
    importBindingNames,
    namedImportsFromRelative,
    namespaceImportFromRelative,
    sideEffectFromRelative
  };
  for (const stmt of sourceFile.statements) {
    visitTopLevel(stmt, topCtx);
    visitImports(stmt, importCtx);
  }
  visitForReferences(sourceFile, identifierRefs);
  return {
    declarations,
    defaultExport,
    identifierRefs,
    namedImportsFromRelative,
    namespaceImportFromRelative,
    reExportAllFromRelative,
    reExportsFromRelative,
    sideEffectFromRelative
  };
}
export {
  collectSymbolsFromFile
};
//# sourceMappingURL=collectSymbols.mjs.map
