import {
  SelectQueryNode as SelectQueryNodeClass,
  InsertQueryNode as InsertQueryNodeClass,
  UpdateQueryNode as UpdateQueryNodeClass,
  DeleteQueryNode as DeleteQueryNodeClass,
} from 'kysely';
import { MikroTransformer } from './transformer.js';
/** Kysely plugin that transforms queries and results to use MikroORM entity/property naming conventions. */
export class MikroKyselyPlugin {
  static #queryNodeCache = new WeakMap();
  #transformer;
  #options;
  constructor(em, options = {}) {
    this.#options = options;
    this.#transformer = new MikroTransformer(em, options);
  }
  transformQuery(args) {
    this.#transformer.reset();
    const result = this.#transformer.transformNode(args.node, args.queryId);
    // Cache the entity map if it is one we can process (for use in transformResult)
    if (
      SelectQueryNodeClass.is(args.node) ||
      InsertQueryNodeClass.is(args.node) ||
      UpdateQueryNodeClass.is(args.node) ||
      DeleteQueryNodeClass.is(args.node)
    ) {
      // clone the entityMap because the transformer's internal map will be cleared and reused by the next query
      const entityMap = new Map(this.#transformer.getOutputEntityMap());
      MikroKyselyPlugin.#queryNodeCache.set(args.queryId, { entityMap });
    }
    return result;
  }
  async transformResult(args) {
    // Only transform results if columnNamingStrategy is 'property' or convertValues is true
    if (this.#options.columnNamingStrategy !== 'property' && !this.#options.convertValues) {
      return args.result;
    }
    // Retrieve the cached query node and metadata
    const cache = MikroKyselyPlugin.#queryNodeCache.get(args.queryId);
    if (!cache) {
      return args.result;
    }
    // Transform the result rows using the transformer
    const transformedRows = this.#transformer.transformResult(args.result.rows ?? [], cache.entityMap);
    return {
      ...args.result,
      rows: transformedRows ?? [],
    };
  }
}
