import { InfrastructureError } from "../errors";
import {
  IMapperRegistry,
  MapperDomainKey,
  MapperKey,
  MapperQueryKey,
} from "./mapper-registry.interface";

export class InMemoryMapperRegistry implements IMapperRegistry {
  private _mappers: Map<string, any> = new Map();

  private _normalizeKey(key: MapperDomainKey | MapperQueryKey): MapperKey {
    return {
      resource: key.resource,
      query: "query" in key && key.query ? key.query : "DOMAIN",
    };
  }

  /**
   * 🔹 Construye la clave única para el registro.
   */
  private _buildKey(key: MapperKey): string {
    const { resource, query } = key as {
      resource: string;
      query?: string;
    };
    return [resource.toLowerCase(), query ?? "DOMAIN"].join("::");
  }

  private _getMapper<T>(key: MapperKey): T {
    const normalizedKey = this._normalizeKey(key);
    const exactKey = this._buildKey(normalizedKey);

    if (!this._mappers.has(exactKey)) {
      throw new InfrastructureError(
        `[InMemoryMapperRegistry] Mapper not registered: ${normalizedKey.resource}::${normalizedKey.query}`
      );
    }

    return this._mappers.get(exactKey);
  }

  private _registerMapper<T>(key: MapperDomainKey | MapperQueryKey, mapper: T) {
    const exactKey = this._buildKey(this._normalizeKey(key));
    this._mappers.set(exactKey, mapper);
  }

  getDomainMapper<T>(key: MapperDomainKey): T {
    return this._getMapper({
      ...key,
      query: "DOMAIN",
    });
  }

  getQueryMapper<T>(key: MapperQueryKey): T {
    return this._getMapper(key);
  }

  registerDomainMapper<T>(key: MapperDomainKey, mapper: T) {
    this._registerMapper(key, mapper);
    return this;
  }

  registerQueryMapper<T>(key: MapperQueryKey, mapper: T) {
    this._registerMapper(key, mapper);
    return this;
  }

  registerQueryMappers(mappers: Array<{ key: MapperQueryKey; mapper: any }>): this {
    mappers.forEach(({ key, mapper }) => this.registerQueryMapper(key, mapper));
    return this;
  }
}
