import { Utils, QueryOrder, DecimalType, DoubleType } from '@mikro-orm/core';
import { MySqlSchemaHelper } from './MySqlSchemaHelper.js';
import { MySqlExceptionConverter } from './MySqlExceptionConverter.js';
import { AbstractSqlPlatform } from '../../AbstractSqlPlatform.js';
import { MySqlNativeQueryBuilder } from './MySqlNativeQueryBuilder.js';
export class BaseMySqlPlatform extends AbstractSqlPlatform {
  schemaHelper = new MySqlSchemaHelper(this);
  exceptionConverter = new MySqlExceptionConverter();
  #jsonTypeCasts = {
    string: 'text',
    number: 'double',
    bigint: 'bigint',
    boolean: 'unsigned',
  };
  ORDER_BY_NULLS_TRANSLATE = {
    [QueryOrder.asc_nulls_first]: 'is not null',
    [QueryOrder.asc_nulls_last]: 'is null',
    [QueryOrder.desc_nulls_first]: 'is not null',
    [QueryOrder.desc_nulls_last]: 'is null',
  };
  /** @internal */
  createNativeQueryBuilder() {
    return new MySqlNativeQueryBuilder(this);
  }
  getDefaultCharset() {
    return 'utf8mb4';
  }
  init(orm) {
    super.init(orm);
    orm.config.get('schemaGenerator').disableForeignKeysForClear ??= true;
  }
  getBeginTransactionSQL(options) {
    if (options?.isolationLevel || options?.readOnly) {
      const parts = [];
      if (options.isolationLevel) {
        parts.push(`isolation level ${options.isolationLevel}`);
      }
      if (options.readOnly) {
        parts.push('read only');
      }
      const sql = `set transaction ${parts.join(', ')}`;
      return [sql, 'begin'];
    }
    return ['begin'];
  }
  convertJsonToDatabaseValue(value, context) {
    if (context?.mode === 'query') {
      return value;
    }
    return JSON.stringify(value);
  }
  getJsonIndexDefinition(index) {
    return index.columnNames.map(column => {
      if (!column.includes('.')) {
        return column;
      }
      const [root, ...path] = column.split('.');
      return `(json_value(${this.quoteIdentifier(root)}, '$.${path.join('.')}' returning ${index.options?.returning ?? 'char(255)'}))`;
    });
  }
  getBooleanTypeDeclarationSQL() {
    return 'tinyint(1)';
  }
  normalizeColumnType(type, options) {
    const simpleType = this.extractSimpleType(type);
    if (['decimal', 'numeric'].includes(simpleType)) {
      return this.getDecimalTypeDeclarationSQL(options);
    }
    return type;
  }
  getDefaultMappedType(type) {
    if (type === 'tinyint(1)') {
      return super.getDefaultMappedType('boolean');
    }
    return super.getDefaultMappedType(type);
  }
  isNumericColumn(mappedType) {
    return super.isNumericColumn(mappedType) || [DecimalType, DoubleType].some(t => mappedType instanceof t);
  }
  supportsUnsigned() {
    return true;
  }
  /**
   * Returns the default name of index for the given columns
   * cannot go past 64 character length for identifiers in MySQL
   */
  getIndexName(tableName, columns, type) {
    if (type === 'primary') {
      return this.getDefaultPrimaryName(tableName, columns);
    }
    const indexName = super.getIndexName(tableName, columns, type);
    if (indexName.length > 64) {
      return `${indexName.substring(0, 56 - type.length)}_${Utils.hash(indexName, 5)}_${type}`;
    }
    return indexName;
  }
  getDefaultPrimaryName(tableName, columns) {
    return 'PRIMARY'; // https://dev.mysql.com/doc/refman/8.0/en/create-table.html#create-table-indexes-keys
  }
  supportsCreatingFullTextIndex() {
    return true;
  }
  getFullTextWhereClause() {
    return `match(:column:) against (:query in boolean mode)`;
  }
  getFullTextIndexExpression(indexName, schemaName, tableName, columns) {
    /* v8 ignore next */
    const quotedTableName = this.quoteIdentifier(schemaName ? `${schemaName}.${tableName}` : tableName);
    const quotedColumnNames = columns.map(c => this.quoteIdentifier(c.name));
    const quotedIndexName = this.quoteIdentifier(indexName);
    return `alter table ${quotedTableName} add fulltext index ${quotedIndexName}(${quotedColumnNames.join(',')})`;
  }
  getOrderByExpression(column, direction, collation) {
    const ret = [];
    const dir = direction.toLowerCase();
    const col = collation ? `${column} collate ${this.quoteCollation(collation)}` : column;
    if (dir in this.ORDER_BY_NULLS_TRANSLATE) {
      ret.push(`${col} ${this.ORDER_BY_NULLS_TRANSLATE[dir]}`);
    }
    ret.push(`${col} ${dir.replace(/(\s|nulls|first|last)*/gi, '')}`);
    return ret;
  }
  getJsonArrayFromSQL(column, alias, properties) {
    const columns = properties
      .map(
        p =>
          `${this.quoteIdentifier(p.name)} ${this.#jsonTypeCasts[p.type] ?? 'text'} path '$.${this.quoteJsonKey(p.name)}'`,
      )
      .join(', ');
    return `json_table(${column}, '$[*]' columns (${columns})) as ${this.quoteIdentifier(alias)}`;
  }
  // MySQL does not support correlated json_table inside EXISTS subqueries,
  // so we use a semi-join via the comma-join pattern instead.
  getJsonArrayExistsSQL(from, where) {
    return `(select 1 from ${from} where ${where} limit 1) is not null`;
  }
  getDefaultClientUrl() {
    return 'mysql://root@127.0.0.1:3306';
  }
}
