import { CommitOrderCalculator } from '../unit-of-work/CommitOrderCalculator.js';
import { EntityManager } from '../EntityManager.js';
export class AbstractSchemaGenerator {
  em;
  driver;
  config;
  metadata;
  platform;
  connection;
  constructor(em) {
    this.em = em instanceof EntityManager ? em : undefined;
    this.driver = em instanceof EntityManager ? em.getDriver() : em;
    this.config = this.driver.config;
    this.metadata = this.driver.getMetadata();
    this.platform = this.driver.getPlatform();
    this.connection = this.driver.getConnection();
  }
  async create(options) {
    this.notImplemented();
  }
  /**
   * Returns true if the database was created.
   */
  async ensureDatabase(options) {
    this.notImplemented();
  }
  async refresh(options) {
    if (options?.dropDb) {
      const name = this.config.get('dbName');
      await this.dropDatabase(name);
      await this.createDatabase(name);
    } else {
      await this.ensureDatabase();
      await this.drop(options);
    }
    if (options?.createSchema !== false) {
      await this.create(options);
    }
  }
  async clear(options) {
    for (const meta of this.getOrderedMetadata(options?.schema).reverse()) {
      await this.driver.nativeDelete(meta.class, {}, options);
    }
    if (options?.clearIdentityMap ?? true) {
      this.clearIdentityMap();
    }
  }
  clearIdentityMap() {
    /* v8 ignore next */
    if (!this.em) {
      return;
    }
    const allowGlobalContext = this.config.get('allowGlobalContext');
    this.config.set('allowGlobalContext', true);
    this.em.clear();
    this.config.set('allowGlobalContext', allowGlobalContext);
  }
  async getCreateSchemaSQL(options) {
    this.notImplemented();
  }
  async drop(options) {
    this.notImplemented();
  }
  async getDropSchemaSQL(options) {
    this.notImplemented();
  }
  async update(options) {
    this.notImplemented();
  }
  async getUpdateSchemaSQL(options) {
    this.notImplemented();
  }
  async getUpdateSchemaMigrationSQL(options) {
    this.notImplemented();
  }
  /**
   * creates new database and connects to it
   */
  async createDatabase(name) {
    this.notImplemented();
  }
  async dropDatabase(name) {
    this.notImplemented();
  }
  async execute(query) {
    this.notImplemented();
  }
  async ensureIndexes() {
    this.notImplemented();
  }
  getOrderedMetadata(schema) {
    const metadata = [...this.metadata.getAll().values()].filter(meta => {
      const isRootEntity = meta.root.class === meta.class;
      const isTPTChild = meta.inheritanceType === 'tpt' && meta.tptParent;
      return (isRootEntity || isTPTChild) && !meta.embeddable && !meta.virtual;
    });
    const calc = new CommitOrderCalculator();
    metadata.forEach(meta => {
      const nodeId = meta.inheritanceType === 'tpt' && meta.tptParent ? meta._id : meta.root._id;
      calc.addNode(nodeId);
    });
    let meta = metadata.pop();
    while (meta) {
      const nodeId = meta.inheritanceType === 'tpt' && meta.tptParent ? meta._id : meta.root._id;
      for (const prop of meta.relations) {
        calc.discoverProperty(prop, nodeId);
      }
      if (meta.inheritanceType === 'tpt' && meta.tptParent) {
        const parentId = meta.tptParent._id;
        calc.addDependency(parentId, nodeId, 1);
      }
      meta = metadata.pop();
    }
    return calc
      .sort()
      .map(cls => this.metadata.getById(cls))
      .filter(meta => {
        const targetSchema = meta.schema ?? this.config.get('schema', this.platform.getDefaultSchemaName());
        return schema ? [schema, '*'].includes(targetSchema) : meta.schema !== '*';
      });
  }
  notImplemented() {
    throw new Error(`This method is not supported by ${this.driver.constructor.name} driver`);
  }
}
