/**
 * 帖子差异计算器
 * 计算两个帖子列表之间的差异，支持增量更新
 */

import {
  PostIdentifier,
  PostDiffResult,
  PostDiffConfig,
  DEFAULT_POST_DIFF_CONFIG,
  PostComparator,
  PostSorter,
  PostDiffStats,
} from './postTypes';

/**
 * 帖子差异计算器类
 * 用于高效计算帖子列表的变化
 */
export class PostDiffCalculator<T extends PostIdentifier> {
  private config: Required<PostDiffConfig>;
  private previousPosts: Map<string, T> = new Map();
  private previousOrder: string[] = [];
  private stats: PostDiffStats = {
    totalBatches: 0,
    totalUpdates: 0,
    averageBatchSize: 0,
    lastBatchTime: 0,
    addedCount: 0,
    updatedCount: 0,
    deletedCount: 0,
  };

  constructor(config: PostDiffConfig = {}) {
    this.config = { ...DEFAULT_POST_DIFF_CONFIG, ...config };
  }

  /**
   * 计算两个帖子列表的差异
   * @param oldList 旧帖子列表
   * @param newList 新帖子列表
   * @returns 差异结果
   */
  calculateDiff(oldList: T[], newList: T[]): PostDiffResult<T> {
    const result: PostDiffResult<T> = {
      added: [],
      updated: [],
      deleted: [],
      moved: [],
      unchanged: [],
      shouldReset: false,
    };

    // 如果旧列表为空，直接返回新列表作为新增
    if (oldList.length === 0) {
      result.added = [...newList];
      this.updateCache(newList);
      this.updateStats(0, newList.length, 0);
      return result;
    }

    // 如果新列表为空，表示全部删除
    if (newList.length === 0) {
      result.deleted = oldList.map((post, index) => ({ post, index }));
      this.clearCache();
      this.updateStats(0, 0, oldList.length);
      return result;
    }

    // 检查是否需要重置（变化太大）
    const changeRatio = this.calculateChangeRatio(oldList, newList);
    if (changeRatio > this.config.resetThreshold) {
      result.shouldReset = true;
      result.added = [...newList];
      this.updateCache(newList);
      this.updateStats(0, newList.length, 0);
      return result;
    }

    // 构建旧列表的索引映射
    const oldIndexMap = new Map<string, number>();
    oldList.forEach((post, index) => {
      oldIndexMap.set(post.id, index);
    });

    // 构建新列表的索引映射
    const newIndexMap = new Map<string, number>();
    newList.forEach((post, index) => {
      newIndexMap.set(post.id, index);
    });

    // 检测新增、更新和未变更的帖子
    const processedIds = new Set<string>();
    let addedCount = 0;
    let updatedCount = 0;

    for (let newIndex = 0; newIndex < newList.length; newIndex++) {
      const newPost = newList[newIndex];
      const oldIndex = oldIndexMap.get(newPost.id);

      if (oldIndex === undefined) {
        // 新增帖子
        result.added.push(newPost);
        addedCount++;
      } else {
        const oldPost = oldList[oldIndex];
        processedIds.add(newPost.id);

        // 检测是否有更新
        const changes = this.detectChanges(oldPost, newPost);
        if (changes && Object.keys(changes).length > 0) {
          result.updated.push({
            post: newPost,
            index: oldIndex,
            changes,
          });
          updatedCount++;
        } else {
          result.unchanged.push(newPost);
        }

        // 检测是否移动
        if (oldIndex !== newIndex) {
          result.moved.push({
            post: newPost,
            fromIndex: oldIndex,
            toIndex: newIndex,
          });
        }
      }
    }

    // 检测删除的帖子
    let deletedCount = 0;
    for (let oldIndex = 0; oldIndex < oldList.length; oldIndex++) {
      const oldPost = oldList[oldIndex];
      if (!processedIds.has(oldPost.id)) {
        result.deleted.push({ post: oldPost, index: oldIndex });
        deletedCount++;
      }
    }

    this.updateCache(newList);
    this.updateStats(addedCount, updatedCount, deletedCount);
    return result;
  }

  /**
   * 计算增量差异（基于缓存的上次状态）
   * @param newList 新帖子列表
   * @returns 差异结果
   */
  calculateIncrementalDiff(newList: T[]): PostDiffResult<T> {
    const oldList = Array.from(this.previousPosts.values());
    // 按照之前的顺序排序
    oldList.sort((a, b) => {
      const indexA = this.previousOrder.indexOf(a.id);
      const indexB = this.previousOrder.indexOf(b.id);
      return indexA - indexB;
    });
    return this.calculateDiff(oldList, newList);
  }

  /**
   * 计算单个帖子的变化
   * @param oldPost 旧帖子
   * @param newPost 新帖子
   * @returns 变化的字段
   */
  detectChanges(oldPost: T, newPost: T): Partial<T> | null {
    const changes: Partial<T> = {};
    let hasChanges = false;

    // 如果配置了追踪特定字段，只检测这些字段
    const fieldsToCheck = this.config.detectPartialUpdates
      ? this.config.trackedFields
      : (Object.keys(newPost) as Array<keyof T>);

    for (const key of fieldsToCheck) {
      if (key === 'id') continue; // ID 不变

      const oldValue = (oldPost as any)[key];
      const newValue = (newPost as any)[key];

      // 深度比较
      if (this.hasValueChanged(oldValue, newValue)) {
        (changes as any)[key] = newValue;
        hasChanges = true;
      }
    }

    return hasChanges ? changes : null;
  }

  /**
   * 检测值是否变化
   * @param oldValue 旧值
   * @param newValue 新值
   * @returns 是否变化
   */
  private hasValueChanged(oldValue: any, newValue: any): boolean {
    // 简单类型直接比较
    if (typeof oldValue !== 'object' || oldValue === null || newValue === null) {
      return oldValue !== newValue;
    }

    // 数组比较
    if (Array.isArray(oldValue) && Array.isArray(newValue)) {
      if (oldValue.length !== newValue.length) return true;
      return JSON.stringify(oldValue) !== JSON.stringify(newValue);
    }

    // 对象比较
    return JSON.stringify(oldValue) !== JSON.stringify(newValue);
  }

  /**
   * 计算变化比例
   * @param oldList 旧列表
   * @param newList 新列表
   * @returns 变化比例（0-1）
   */
  private calculateChangeRatio(oldList: T[], newList: T[]): number {
    if (oldList.length === 0 && newList.length === 0) return 0;
    if (oldList.length === 0 || newList.length === 0) return 1;

    const oldIds = new Set(oldList.map(p => p.id));
    const newIds = new Set(newList.map(p => p.id));

    let commonCount = 0;
    oldIds.forEach(id => {
      if (newIds.has(id)) {
        commonCount++;
      }
    });

    const maxLength = Math.max(oldList.length, newList.length);
    return 1 - commonCount / maxLength;
  }

  /**
   * 更新缓存
   * @param posts 帖子列表
   */
  private updateCache(posts: T[]): void {
    this.previousPosts.clear();
    this.previousOrder = [];

    for (const post of posts) {
      this.previousPosts.set(post.id, post);
      this.previousOrder.push(post.id);
    }
  }

  /**
   * 清除缓存
   */
  private clearCache(): void {
    this.previousPosts.clear();
    this.previousOrder = [];
  }

  /**
   * 更新统计信息
   */
  private updateStats(addedCount: number, updatedCount: number, deletedCount: number): void {
    this.stats.totalBatches++;
    const totalChanges = addedCount + updatedCount + deletedCount;
    this.stats.totalUpdates += totalChanges;
    this.stats.averageBatchSize = this.stats.totalUpdates / this.stats.totalBatches;
    this.stats.lastBatchTime = Date.now();
    this.stats.addedCount += addedCount;
    this.stats.updatedCount += updatedCount;
    this.stats.deletedCount += deletedCount;
  }

  /**
   * 设置配置
   * @param config 配置
   */
  setConfig(config: Partial<PostDiffConfig>): void {
    this.config = { ...this.config, ...config };
  }

  /**
   * 获取当前配置
   * @returns 当前配置
   */
  getConfig(): Required<PostDiffConfig> {
    return { ...this.config };
  }

  /**
   * 重置计算器状态
   */
  reset(): void {
    this.clearCache();
    this.stats = {
      totalBatches: 0,
      totalUpdates: 0,
      averageBatchSize: 0,
      lastBatchTime: 0,
      addedCount: 0,
      updatedCount: 0,
      deletedCount: 0,
    };
  }

  /**
   * 获取缓存的帖子数量
   * @returns 缓存数量
   */
  getCachedCount(): number {
    return this.previousPosts.size;
  }

  /**
   * 检查帖子是否在缓存中
   * @param postId 帖子 ID
   * @returns 是否存在
   */
  hasPost(postId: string): boolean {
    return this.previousPosts.has(postId);
  }

  /**
   * 获取缓存中的帖子
   * @param postId 帖子 ID
   * @returns 帖子或 undefined
   */
  getCachedPost(postId: string): T | undefined {
    return this.previousPosts.get(postId);
  }

  /**
   * 获取统计信息
   * @returns 统计信息
   */
  getStats(): PostDiffStats {
    return { ...this.stats };
  }

  /**
   * 批量检测帖子变化
   * @param posts 要检测的帖子列表
   * @returns 变化的帖子列表
   */
  batchDetectChanges(posts: T[]): Array<{ post: T; changes: Partial<T> }> {
    const results: Array<{ post: T; changes: Partial<T> }> = [];

    for (const post of posts) {
      const cachedPost = this.previousPosts.get(post.id);
      if (cachedPost) {
        const changes = this.detectChanges(cachedPost, post);
        if (changes && Object.keys(changes).length > 0) {
          results.push({ post, changes });
        }
      }
    }

    return results;
  }

  /**
   * 比较两个帖子列表是否相同
   * @param list1 列表1
   * @param list2 列表2
   * @returns 是否相同
   */
  areEqual(list1: T[], list2: T[]): boolean {
    if (list1.length !== list2.length) return false;

    for (let i = 0; i < list1.length; i++) {
      if (list1[i].id !== list2[i].id) return false;
    }

    return true;
  }

  /**
   * 获取两个帖子列表的交集ID
   * @param list1 列表1
   * @param list2 列表2
   * @returns 交集ID集合
   */
  getIntersection(list1: T[], list2: T[]): Set<string> {
    const ids1 = new Set(list1.map(p => p.id));
    const ids2 = new Set(list2.map(p => p.id));
    const intersection = new Set<string>();

    ids1.forEach(id => {
      if (ids2.has(id)) {
        intersection.add(id);
      }
    });

    return intersection;
  }

  /**
   * 获取两个帖子列表的差集ID
   * @param list1 列表1
   * @param list2 列表2
   * @returns 差集ID集合（在list1中但不在list2中）
   */
  getDifference(list1: T[], list2: T[]): Set<string> {
    const ids1 = new Set(list1.map(p => p.id));
    const ids2 = new Set(list2.map(p => p.id));
    const difference = new Set<string>();

    ids1.forEach(id => {
      if (!ids2.has(id)) {
        difference.add(id);
      }
    });

    return difference;
  }
}

/**
 * 创建帖子差异计算器实例的工厂函数
 * @param config 配置
 * @returns PostDiffCalculator 实例
 */
export function createPostDiffCalculator<T extends PostIdentifier>(
  config?: PostDiffConfig
): PostDiffCalculator<T> {
  return new PostDiffCalculator<T>(config);
}
