import {
  ImportSpecifierStructure,
  Project,
  PropertyDeclaration,
  PropertyDeclarationStructure,
  SourceFile,
} from 'ts-morph';

export function getFieldOptions(
  sourceFile: SourceFile,
  property: string | PropertyDeclaration,
) {
  let propertyDeclaration: PropertyDeclaration | undefined;
  if (typeof property === 'string') {
    propertyDeclaration = sourceFile.getClass(() => true)?.getProperty(property);
  }
  const result = propertyDeclaration?.getStructure()?.decorators?.[0]?.arguments?.[1];
  return result as string;
  // return new Function(`return ${text}`)();
}

export function getPropertyStructure(sourceFile: SourceFile, name: string) {
  return sourceFile
    .getClass(() => true)
    ?.getProperty(p => p.getName() === name)
    ?.getStructure();
}

export function testSourceFile(args: {
  project: Project;
  file?: string;
  class?: string;
  property?: string;
}) {
  const { project, file, property, class: className } = args;
  let sourceFile: SourceFile;
  if (file) {
    sourceFile = project.getSourceFileOrThrow(s => s.getFilePath().endsWith(file));
  } else if (className) {
    sourceFile = project.getSourceFileOrThrow(s => Boolean(s.getClass(className)));
  } else {
    throw new TypeError('file or class must be provided');
  }

  const importDeclarations = sourceFile
    .getImportDeclarations()
    .map(d => d.getStructure());
  const classFile = sourceFile.getClass(() => true)!;
  const propertyStructure =
    property && classFile.getProperty(p => p.getName() === property)?.getStructure();
  const propertyDecorators = (
    propertyStructure as PropertyDeclarationStructure | undefined
  )?.decorators;
  const fieldDecorator = propertyDecorators?.find(d => d.name === 'Field');

  type ImportElement = { name: string; specifier: string };
  const namedImports: ImportElement[] = [];
  const namespaceImports: ImportElement[] = [];

  for (const d of importDeclarations) {
    if (d.namespaceImport) {
      namespaceImports.push({
        name: d.namespaceImport,
        specifier: d.moduleSpecifier,
      });
    }
    for (const s of (d.namedImports || []) as ImportSpecifierStructure[]) {
      namedImports.push({
        name: s.name,
        specifier: d.moduleSpecifier,
      });
    }
  }

  return {
    sourceFile,
    classFile,
    sourceText: sourceFile.getText(),
    namedImports,
    namespaceImports,
    property: propertyStructure as PropertyDeclarationStructure | undefined,
    propertyDecorators,
    fieldDecorator,
    fieldDecoratorType: fieldDecorator?.arguments?.[0],
    fieldDecoratorOptions: fieldDecorator?.arguments?.[1],
  };
}