import { DataAbstractionLayer, ActionDataMapping, DataContext, DataPriority, EnhancedDataSource, DataCacheConfig, ActionDataTable, DataSyncConfig, DataSyncResult } from '../shared/types/shared/DataAbstractionLayer';
import { IDataSource } from '../shared/types/shared/DataSource';
import { cacheService } from './CacheService';

export class DataAbstractionLayerService implements DataAbstractionLayer {
  private dataSources: Map<string, EnhancedDataSource<any>> = new Map();
  private actionDataMappings: Map<string, ActionDataMapping> = new Map();
  private dataContexts: Map<string, DataContext> = new Map();
  private cacheConfig: DataCacheConfig = {
    enabled: true,
    ttl: 3600,
    sizeLimit: 100
  };

  registerDataSource(name: string, dataSource: EnhancedDataSource<any>): void {
    this.dataSources.set(name, dataSource);
  }

  getDataSource(name: string): EnhancedDataSource<any> | null {
    return this.dataSources.get(name) || null;
  }

  getActionDataMapping(actionType: string): ActionDataMapping | null {
    return this.actionDataMappings.get(actionType) || null;
  }

  registerActionDataMapping(mapping: ActionDataMapping): void {
    this.actionDataMappings.set(mapping.actionType, mapping);
  }

  async getPrioritizedData(actionType: string, taskId: string, params?: Record<string, any>): Promise<Record<string, any>> {
    const mapping = this.getActionDataMapping(actionType);
    if (!mapping) {
      throw new Error(`No data mapping found for action type: ${actionType}`);
    }

    const context: DataContext = {
      actionType,
      taskId,
      timestamp: Date.now(),
      dataRequirements: mapping.requiredDataSources.map(dataSource => ({
        dataSource,
        priority: mapping.dataPriority[dataSource] || {
          level: 'medium',
          weight: 1,
          expiration: 3600
        },
        filters: params
      }))
    };

    await this.updateDataContext(context);

    const result: Record<string, any> = {};

    for (const dataSourceName of mapping.requiredDataSources) {
      const dataSource = this.getDataSource(dataSourceName);
      if (dataSource) {
        const priority = mapping.dataPriority[dataSourceName];
        const cacheKey = `data_cache:${actionType}:${taskId}:${dataSourceName}`;

        // 尝试从缓存获取数据
        const cachedData = await cacheService.get<any>(cacheKey);
        if (cachedData) {
          result[dataSourceName] = cachedData;
          continue;
        }

        // 从数据源获取数据
        const data = await dataSource.getPrioritizedData(params, priority!, context);
        result[dataSourceName] = data;

        // 缓存数据
        await cacheService.set(cacheKey, data, {
          ttl: priority!.expiration,
          tags: [actionType, dataSourceName, taskId],
          priority: priority!.level as 'low' | 'medium' | 'high'
        });
      }
    }

    return result;
  }

  async updateDataContext(context: DataContext): Promise<void> {
    this.dataContexts.set(context.taskId, context);
    
    // 存储到缓存服务
    await cacheService.set(`data_context:${context.taskId}`, context, {
      ttl: 86400, // 24小时过期
      tags: [context.actionType, context.taskId],
      priority: 'high'
    });
  }

  async getDataContext(taskId: string): Promise<DataContext | null> {
    // 先从内存获取
    let context = this.dataContexts.get(taskId);
    
    // 如果内存中没有，尝试从缓存服务获取
    if (!context) {
      try {
        const data = await cacheService.get<DataContext>(`data_context:${taskId}`);
        if (data) {
          context = data;
          this.dataContexts.set(taskId, context);
        }
      } catch (error) {
        console.error('Failed to get data context from cache:', error);
      }
    }
    
    return context;
  }

  setCacheConfig(config: DataCacheConfig): void {
    this.cacheConfig = config;
  }

  async clearCache(dataSource?: string): Promise<void> {
    if (dataSource) {
      // 清除特定数据源的缓存
      // 使用标签删除
      await cacheService.deleteByTag(dataSource);
    } else {
      // 清除所有缓存
      await cacheService.clear();
    }
  }
}

export const dataAbstractionLayerService = new DataAbstractionLayerService();
