import { Utils } from '../utils/Utils.js';
import { EntitySchema } from './EntitySchema.js';
/** Base metadata provider that resolves entity type information and manages metadata caching. */
export class MetadataProvider {
  config;
  constructor(config) {
    this.config = config;
  }
  /** Resolves entity references and type information for all properties in the given metadata. */
  loadEntityMetadata(meta) {
    for (const prop of meta.props) {
      /* v8 ignore next */
      if (typeof prop.entity === 'string') {
        prop.type = prop.entity;
      } else if (prop.entity) {
        const tmp = prop.entity();
        prop.type = Array.isArray(tmp)
          ? tmp
              .map(t => Utils.className(t))
              .sort()
              .join(' | ')
          : Utils.className(tmp);
        prop.target = EntitySchema.is(tmp) ? tmp.meta.class : tmp;
      } else if (!prop.type && !((prop.enum || prop.array) && (prop.items?.length ?? 0) > 0)) {
        throw new Error(`Please provide either 'type' or 'entity' attribute in ${meta.className}.${prop.name}.`);
      }
    }
  }
  /** Merges cached metadata into the given entity metadata, preserving function expressions. */
  loadFromCache(meta, cache) {
    Object.values(cache.properties).forEach(prop => {
      const metaProp = meta.properties[prop.name];
      /* v8 ignore next */
      if (metaProp?.enum && Array.isArray(metaProp.items)) {
        delete prop.items;
      }
    });
    // Preserve function expressions from indexes/uniques — they can't survive JSON cache serialization
    const expressionMap = new Map();
    for (const arr of [meta.indexes, meta.uniques]) {
      for (const idx of arr ?? []) {
        if (typeof idx.expression === 'function' && idx.name) {
          expressionMap.set(idx.name, idx.expression);
        }
      }
    }
    Utils.mergeConfig(meta, cache);
    // Restore function expressions that were lost during JSON serialization
    if (expressionMap.size > 0) {
      for (const arr of [meta.indexes, meta.uniques]) {
        for (const idx of arr ?? []) {
          const fn = idx.name && expressionMap.get(idx.name);
          if (fn && typeof idx.expression !== 'function') {
            idx.expression = fn;
          }
        }
      }
    }
  }
  /** Whether this provider class uses metadata caching by default. */
  static useCache() {
    return false;
  }
  /** Whether metadata caching is enabled for this instance. */
  useCache() {
    return this.config.get('metadataCache').enabled ?? MetadataProvider.useCache();
  }
  saveToCache(meta) {
    //
  }
  /** Attempts to load metadata from cache, returning undefined if not available. */
  getCachedMetadata(meta, root) {
    if (!this.useCache()) {
      return undefined;
    }
    const cache = meta.path && this.config.getMetadataCacheAdapter().get(this.getCacheKey(meta));
    if (cache) {
      this.loadFromCache(meta, cache);
      meta.root = root;
    }
    return cache;
  }
  /** Combines individual metadata cache entries into a single file. */
  combineCache() {
    const path = this.config.getMetadataCacheAdapter().combine?.();
    // override the path in the options, so we can log it from the CLI in `cache:generate` command
    if (path) {
      this.config.get('metadataCache').combined = path;
    }
  }
  /** Returns the cache key for the given entity metadata. */
  getCacheKey(meta) {
    return meta.className;
  }
}
