import { isArray } from '../helpers.mts';
import type { ParseNode } from '../parser/ParseNode.mts';
import { TopLevelVarScopedDeclarations, type VarScopedDeclaration } from './all.mts';

export function VarScopedDeclarations(node: ParseNode | readonly ParseNode[]): VarScopedDeclaration[] {
  if (isArray(node)) {
    const declarations = [];
    for (const item of node) {
      declarations.push(...VarScopedDeclarations(item));
    }
    return declarations;
  }
  switch (node.type) {
    case 'VariableStatement':
      return VarScopedDeclarations(node.VariableDeclarationList);
    case 'VariableDeclaration':
      return [node];
    case 'Block':
      return VarScopedDeclarations(node.StatementList);
    case 'IfStatement': {
      const declarations = VarScopedDeclarations(node.Statement_a);
      if (node.Statement_b) {
        declarations.push(...VarScopedDeclarations(node.Statement_b));
      }
      return declarations;
    }
    case 'WhileStatement':
      return VarScopedDeclarations(node.Statement);
    case 'DoWhileStatement':
      return VarScopedDeclarations(node.Statement);
    case 'ForStatement': {
      const names = [];
      if (node.VariableDeclarationList) {
        names.push(...VarScopedDeclarations(node.VariableDeclarationList));
      }
      names.push(...VarScopedDeclarations(node.Statement));
      return names;
    }
    case 'ForInStatement':
    case 'ForOfStatement':
    case 'ForAwaitStatement': {
      const declarations = [];
      if (node.ForBinding) {
        declarations.push(node.ForBinding);
      }
      declarations.push(...VarScopedDeclarations(node.Statement));
      return declarations;
    }
    case 'WithStatement':
      return VarScopedDeclarations(node.Statement);
    case 'SwitchStatement':
      return VarScopedDeclarations(node.CaseBlock);
    case 'CaseBlock': {
      const names = [];
      if (node.CaseClauses_a) {
        names.push(...VarScopedDeclarations(node.CaseClauses_a));
      }
      if (node.DefaultClause) {
        names.push(...VarScopedDeclarations(node.DefaultClause));
      }
      if (node.CaseClauses_b) {
        names.push(...VarScopedDeclarations(node.CaseClauses_b));
      }
      return names;
    }
    case 'CaseClause':
    case 'DefaultClause':
      if (node.StatementList) {
        return VarScopedDeclarations(node.StatementList);
      }
      return [];
    case 'LabelledStatement':
      return VarScopedDeclarations(node.LabelledItem);
    case 'TryStatement': {
      const declarations = VarScopedDeclarations(node.Block);
      if (node.Catch) {
        declarations.push(...VarScopedDeclarations(node.Catch));
      }
      if (node.Finally) {
        declarations.push(...VarScopedDeclarations(node.Finally));
      }
      return declarations;
    }
    case 'Catch':
      return VarScopedDeclarations(node.Block);
    case 'ExportDeclaration':
      if (node.VariableStatement) {
        return VarScopedDeclarations(node.VariableStatement);
      }
      return [];
    case 'Script':
      if (node.ScriptBody) {
        return VarScopedDeclarations(node.ScriptBody);
      }
      return [];
    case 'ScriptBody':
      return TopLevelVarScopedDeclarations(node.StatementList);
    case 'Module':
      if (node.ModuleBody) {
        return VarScopedDeclarations(node.ModuleBody);
      }
      return [];
    case 'ModuleBody':
      return VarScopedDeclarations(node.ModuleItemList);
    case 'FunctionBody':
    case 'GeneratorBody':
    case 'AsyncBody':
    case 'AsyncGeneratorBody':
      return TopLevelVarScopedDeclarations(node.FunctionStatementList);
    case 'ClassStaticBlockBody':
      return TopLevelVarScopedDeclarations(node.ClassStaticBlockStatementList);
    default:
      return [];
  }
}
