// src/core/registry.ts
import {
  IExtractor,
  ITransformer,
  ILoader,
  ICleaner,
  SourceConfig,
  TargetConfig,
  PipelineStepConfig,
  PipelineContext
} from './interfaces';
import { ConfigurationError, ComponentError } from './errors';
import { FileExtractor } from '../extractors/fileExtractor';
import { ApiExtractor } from '../extractors/apiExtractor';
import { PrismaExtractor } from '../extractors/prismaExtractor';
import { FileLoader } from '../loaders/fileLoader';
import { ApiLoader } from '../loaders/apiLoader';
import { PrismaLoader } from '../loaders/prismaLoader';
import { MappingTransformer } from '../transformers/mappingTransformer';
import { CustomFunctionTransformer } from '../transformers/customFunctionTransformer';
import { MongoExtractor } from '../extractors/mongoExtractor';
import { RedisExtractor } from '../extractors/redisExtractor';
import { MongoLoader } from '../loaders/mongoLoader';
import { RedisLoader } from '../loaders/redisLoader';
import { DefaultValueCleaner, RequiredFieldCleaner } from '../extractors/cleaner';
import { ValidationTransformer } from '../transformers/validationTransformer';

// Define types for constructor signatures
type ExtractorConstructor = new (config: SourceConfig) => IExtractor<any>;
type LoaderConstructor = new (config: TargetConfig) => ILoader<any>;
type TransformerConstructor = new (config: any) => ITransformer<any, any>; // Config might vary
type CleanerConstructor = new (config: any) => ICleaner<any>; // Config might vary

// MARK: - Registry
const extractorRegistry = new Map<string, ExtractorConstructor>();
const transformerRegistry = new Map<string, TransformerConstructor>();
const loaderRegistry = new Map<string, LoaderConstructor>();
const cleanerRegistry = new Map<string, CleanerConstructor>();


// MARK: - Extractors
registerExtractor('prisma', PrismaExtractor as unknown as ExtractorConstructor); // Cast needed due to generic constructor
registerExtractor('postgresql', PrismaExtractor as unknown as ExtractorConstructor);
registerExtractor('mysql', PrismaExtractor as unknown as ExtractorConstructor);
registerExtractor('file', FileExtractor as unknown as ExtractorConstructor);
registerExtractor('api', ApiExtractor as unknown as ExtractorConstructor);
registerExtractor('mongodb', MongoExtractor as unknown as ExtractorConstructor);
registerExtractor('redis', RedisExtractor as unknown as ExtractorConstructor);

// MARK: - Loaders
registerLoader('prisma', PrismaLoader as unknown as LoaderConstructor);
registerLoader('postgresql', PrismaLoader as unknown as LoaderConstructor);
registerLoader('mysql', PrismaLoader as unknown as LoaderConstructor);
// 
registerLoader('file', FileLoader as  LoaderConstructor);
registerLoader('api', ApiLoader as LoaderConstructor);
registerLoader('mongodb', MongoLoader as LoaderConstructor);
registerLoader('redis', RedisLoader as LoaderConstructor);

// MARK: - Transformers
registerTransformer('mapping', MappingTransformer as TransformerConstructor);
registerTransformer('custom-function', CustomFunctionTransformer as TransformerConstructor);
registerTransformer('validator', ValidationTransformer as TransformerConstructor);


// MARK: - Cleaners
registerCleaner('required-fields', RequiredFieldCleaner as CleanerConstructor);
registerCleaner('default-values', DefaultValueCleaner as CleanerConstructor);


// --- Registration Functions ---

// MARK: - + Extractor
export function registerExtractor(type: string, constructor: ExtractorConstructor): void {
  if (extractorRegistry.has(type)) {
      console.warn(`[Registry] Overwriting extractor type: ${type}`);
  }
  extractorRegistry.set(type, constructor);
}

// MARK: - = Transformer
export function registerTransformer(type: string, constructor: TransformerConstructor): void {
  if (transformerRegistry.has(type)) {
      console.warn(`[Registry] Overwriting transformer type: ${type}`);
  }
  transformerRegistry.set(type, constructor);
}

// MARK: - Regis Loader
export function registerLoader(type: string, constructor: LoaderConstructor): void {
  if (loaderRegistry.has(type)) {
      console.warn(`[Registry] Overwriting loader type: ${type}`);
  }
  loaderRegistry.set(type, constructor);
}

// MARK: - Regis Cleaner
export function registerCleaner(type: string, constructor: CleanerConstructor): void {
   if (cleanerRegistry.has(type)) {
       console.warn(`[Registry] Overwriting cleaner type: ${type}`);
   }
   cleanerRegistry.set(type, constructor);
}


// --- Instantiation Functions ---

// MARK: - + Extractor
export function createExtractor<T>(config: SourceConfig): IExtractor<T> {
  const Constructor = extractorRegistry.get(config.type);
  if (!Constructor) {
      throw new ConfigurationError(`Unsupported extractor type: ${config.type}`);
  }
  try {
      return new Constructor(config) as IExtractor<T>;
  } catch (error: any) {
      throw new ComponentError(`Failed to instantiate extractor type ${config.type}`, Constructor.name, error);
  }
}

// MARK: - + Loader
export function createLoader<T>(config: TargetConfig): ILoader<T> {
  const Constructor = loaderRegistry.get(config.type);
  if (!Constructor) {
      throw new ConfigurationError(`Unsupported loader type: ${config.type}`);
  }
   try {
      return new Constructor(config) as ILoader<T>;
  } catch (error: any) {
      throw new ComponentError(`Failed to instantiate loader type ${config.type}`, Constructor.name, error);
  }
}

// MARK: - +Pipeline S
export function createPipelineStep(stepConfig: PipelineStepConfig): ICleaner<any> | ITransformer<any, any> {
  let Constructor: CleanerConstructor | TransformerConstructor | undefined;
  let config: any = stepConfig; // Use the whole step config initially
  let componentType: string = ''; // For error messages

  // Determine constructor and config based on step type
  if (stepConfig.type === 'clean' && stepConfig.cleaner) {
      if (typeof stepConfig.cleaner === 'string') {
          componentType = stepConfig.cleaner;
          Constructor = cleanerRegistry.get(componentType);
          config = stepConfig; // Pass full step config to cleaner constructor potentially
      } else {
          throw new ConfigurationError(`Cleaner definition must be a string type in step config`, undefined, JSON.stringify(stepConfig));
      }
  } else if (stepConfig.type === 'transform' && stepConfig.transformer) {
       if (typeof stepConfig.transformer === 'string') {
          componentType = stepConfig.transformer;
          Constructor = transformerRegistry.get(componentType);
          config = stepConfig;
      } else {
          throw new ConfigurationError(`Transformer definition must be a string type in step config`, undefined, JSON.stringify(stepConfig));
      }
  } else if (stepConfig.type === 'map') {
      componentType = 'mapping'; // Assuming 'mapping' is the registered type for MappingTransformer
      Constructor = transformerRegistry.get(componentType);
      config = stepConfig.rules; // Pass only rules to MappingTransformer constructor usually
       if (!config) throw new ConfigurationError(`'map' step requires 'rules' property.`);
  } else if (stepConfig.type === 'custom' && stepConfig.customFunctionPath) {
      componentType = 'custom-function'; // Special case handled below or by a dedicated transformer
       Constructor = transformerRegistry.get(componentType); // Requires CustomFunctionTransformer to be registered
       config = stepConfig; // Pass full step config
  }
  else {
      throw new ConfigurationError(`Invalid or incomplete pipeline step configuration: ${JSON.stringify(stepConfig)}`);
  }

  if (!Constructor) {
      throw new ConfigurationError(`Unsupported pipeline step type or component not registered: ${componentType}`);
  }

  try {
       // We might need more specific config extraction depending on constructor needs
      return new Constructor(config);
  } catch (error: any) {
      throw new ComponentError(`Failed to instantiate pipeline step component ${componentType}`, Constructor.name, error);
  }
}

// --- Register Built-in Components ---
// This section should be populated as components are created

// Example (will add actual components later):
// import { PrismaExtractor } from '../extractors/prismaExtractor';
// registerExtractor('prisma', PrismaExtractor); // Assuming PrismaExtractor takes generic config
// registerExtractor('postgresql', PrismaExtractor); // Alias
// registerExtractor('mysql', PrismaExtractor); // Alias

// import { MappingTransformer } from '../transformers/mappingTransformer';
// registerTransformer('mapping', MappingTransformer);

// import { PrismaLoader } from '../loaders/prismaLoader';
// registerLoader('prisma', PrismaLoader);
// registerLoader('postgresql', PrismaLoader);
// registerLoader('mysql', PrismaLoader);

// import { RequiredFieldCleaner } from '../transformers/cleaner';
// registerCleaner('required-fields', RequiredFieldCleaner); // Needs config adaptation in createPipelineStep or constructor

console.log('[Registry] Component registry initialized.'); // Log on load