import { Utils } from '../utils/Utils.js';
/** Abstract base class for database connections, providing transaction and query execution support. */
export class Connection {
  config;
  type;
  metadata;
  platform;
  options;
  logger;
  connected = false;
  get #connectionLabel() {
    return {
      type: this.type,
      name: this.options.name || this.config.get('name') || this.options.host || this.options.dbName,
    };
  }
  constructor(config, options, type = 'write') {
    this.config = config;
    this.type = type;
    this.logger = this.config.getLogger();
    this.platform = this.config.getPlatform();
    if (options) {
      this.options = Utils.copy(options);
    } else {
      const props = [
        'dbName',
        'clientUrl',
        'host',
        'port',
        'user',
        'password',
        'multipleStatements',
        'pool',
        'schema',
        'driverOptions',
      ];
      this.options = props.reduce((o, i) => {
        o[i] = this.config.get(i);
        return o;
      }, {});
    }
  }
  /**
   * Closes the database connection (aka disconnect)
   */
  async close(force) {
    Object.keys(this.options)
      .filter(k => k !== 'name')
      .forEach(k => delete this.options[k]);
  }
  /**
   * Ensure the connection exists, this is used to support lazy connect when using `new MikroORM()` instead of the async `init` method.
   */
  async ensureConnection() {
    if (!this.connected) {
      await this.connect();
    }
  }
  /**
   * Execute raw SQL queries, handy from running schema dump loaded from a file.
   * This method doesn't support transactions, as opposed to `orm.schema.execute()`, which is used internally.
   */
  async executeDump(dump) {
    throw new Error(`Executing SQL dumps is not supported by current driver`);
  }
  async onConnect() {
    const schemaGenerator = this.config.getExtension('@mikro-orm/schema-generator');
    if (this.type === 'write' && schemaGenerator) {
      if (this.config.get('ensureDatabase')) {
        const options = this.config.get('ensureDatabase');
        await schemaGenerator.ensureDatabase(typeof options === 'boolean' ? {} : { ...options, forceCheck: true });
      }
      if (this.config.get('ensureIndexes')) {
        await schemaGenerator.ensureIndexes();
      }
    }
  }
  /** Executes a callback inside a transaction, committing on success and rolling back on failure. */
  async transactional(cb, options) {
    throw new Error(`Transactions are not supported by current driver`);
  }
  /** Begins a new database transaction and returns the transaction context. */
  async begin(options) {
    throw new Error(`Transactions are not supported by current driver`);
  }
  /** Commits the given transaction. */
  async commit(ctx, eventBroadcaster, loggerContext) {
    throw new Error(`Transactions are not supported by current driver`);
  }
  /** Rolls back the given transaction. */
  async rollback(ctx, eventBroadcaster, loggerContext) {
    throw new Error(`Transactions are not supported by current driver`);
  }
  /** Parses and returns the resolved connection configuration (host, port, user, etc.). */
  getConnectionOptions() {
    const ret = {};
    if (this.options.clientUrl) {
      const url = new URL(this.options.clientUrl);
      this.options.host = ret.host = this.options.host ?? decodeURIComponent(url.hostname);
      this.options.port = ret.port = this.options.port ?? +url.port;
      this.options.user = ret.user = this.options.user ?? decodeURIComponent(url.username);
      this.options.password = ret.password = this.options.password ?? decodeURIComponent(url.password);
      this.options.dbName = ret.database = this.options.dbName ?? decodeURIComponent(url.pathname).replace(/^\//, '');
      if (this.options.schema || url.searchParams.has('schema')) {
        this.options.schema = ret.schema = this.options.schema ?? decodeURIComponent(url.searchParams.get('schema'));
        this.config.set('schema', ret.schema);
      }
    } else {
      const url = new URL(this.config.get('clientUrl'));
      this.options.host = ret.host = this.options.host ?? this.config.get('host', decodeURIComponent(url.hostname));
      this.options.port = ret.port = this.options.port ?? this.config.get('port', +url.port);
      this.options.user = ret.user = this.options.user ?? this.config.get('user', decodeURIComponent(url.username));
      this.options.password = ret.password =
        this.options.password ?? this.config.get('password', decodeURIComponent(url.password));
      this.options.dbName = ret.database =
        this.options.dbName ?? this.config.get('dbName', decodeURIComponent(url.pathname).replace(/^\//, ''));
    }
    return ret;
  }
  /** Sets the metadata storage on this connection. */
  setMetadata(metadata) {
    this.metadata = metadata;
  }
  /** Sets the platform abstraction on this connection. */
  setPlatform(platform) {
    this.platform = platform;
  }
  /** Returns the platform abstraction for this connection. */
  getPlatform() {
    return this.platform;
  }
  async executeQuery(query, cb, context) {
    const now = Date.now();
    try {
      const res = await cb();
      const took = Date.now() - now;
      const results = Array.isArray(res) ? res.length : undefined;
      const affected = Utils.isPlainObject(res) ? res.affectedRows : undefined;
      this.logQuery(query, { ...context, took, results, affected });
      return res;
    } catch (e) {
      const took = Date.now() - now;
      this.logQuery(query, { ...context, took, level: 'error' });
      throw e;
    }
  }
  logQuery(query, context = {}) {
    const connection = this.#connectionLabel;
    this.logger.logQuery({
      level: 'info',
      connection,
      ...context,
      query,
    });
    const threshold = this.config.get('slowQueryThreshold');
    if (threshold != null && (context.took ?? 0) >= threshold) {
      this.config.getSlowQueryLogger().logQuery({
        ...context,
        // `enabled: true` bypasses the debug-mode check in isEnabled(),
        // ensuring slow query logs are always emitted regardless of the `debug` setting.
        enabled: true,
        level: context.level ?? 'warning',
        namespace: 'slow-query',
        connection,
        query,
      });
    }
  }
}
