import { useState, useRef, useCallback, useEffect } from 'react';
import {
  PostIdentifier,
  PostUpdate,
  PostUpdateType,
  PostDiffConfig,
} from '../infrastructure/diff/postTypes';
import {
  PostDiffCalculator,
  createPostDiffCalculator,
} from '../infrastructure/diff/PostDiffCalculator';
import {
  PostUpdateBatcher,
  PostBatcherOptions,
  createPostUpdateBatcher,
} from '../infrastructure/diff/PostUpdateBatcher';
import { postSyncService } from '@/services/post';
import type { PostsState } from '@/services/post';
import { usePostListStore } from '../stores/post/postListStore';
import type { Post } from '../core/entities/Post';

export interface UseDifferentialPostsOptions<T extends PostIdentifier> {
  diffConfig?: PostDiffConfig;
  batcherOptions?: PostBatcherOptions;
  enableDiff?: boolean;
  enableBatching?: boolean;
  maxPostCount?: number;
  listKey?: string;
  autoSubscribe?: boolean;
}

export interface DiffUpdatesInfo {
  addedCount: number;
  updatedCount: number;
  deletedCount: number;
  lastUpdateTime: number;
}

export interface UseDifferentialPostsResult<T extends PostIdentifier> {
  posts: T[];
  loading: boolean;
  isInitialLoading: boolean;
  isLoadingMore: boolean;
  refreshing: boolean;
  error: string | null;
  hasMore: boolean;
  pendingUpdateCount: number;
  isProcessing: boolean;
  refresh: () => Promise<void>;
  loadMore: () => Promise<void>;
  flush: () => void;
  reset: () => void;
  forceUpdate: (posts: T[]) => void;
  getDiffStats: () => { totalBatches: number; totalUpdates: number; averageBatchSize: number };
  diffUpdates: DiffUpdatesInfo;
}

export function useDifferentialPosts<T extends PostIdentifier = Post>(
  initialPosts: T[] = [],
  options: UseDifferentialPostsOptions<T> = {}
): UseDifferentialPostsResult<T> {
  const {
    diffConfig,
    batcherOptions,
    enableDiff = true,
    enableBatching = true,
    maxPostCount = 10000,
    listKey = 'default',
    autoSubscribe = true,
  } = options;

  const [posts, setPosts] = useState<T[]>(initialPosts);
  const [loading, setLoading] = useState(false);
  const [refreshing, setRefreshing] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [hasMore, setHasMore] = useState(true);
  const [isProcessing, setIsProcessing] = useState(false);
  const [pendingUpdateCount, setPendingUpdateCount] = useState(0);
  const [diffUpdates, setDiffUpdates] = useState<DiffUpdatesInfo>({
    addedCount: 0,
    updatedCount: 0,
    deletedCount: 0,
    lastUpdateTime: 0,
  });

  const calculatorRef = useRef<PostDiffCalculator<T> | null>(null);
  const batcherRef = useRef<PostUpdateBatcher | null>(null);
  const previousPostsRef = useRef<T[]>(initialPosts);

  const handleBatchUpdates = useCallback((updates: PostUpdate[]) => {
    setIsProcessing(true);

    try {
      setPosts(currentPosts => {
        let newPosts = [...currentPosts];
        let addedCount = 0;
        let updatedCount = 0;
        let deletedCount = 0;

        for (const update of updates) {
          switch (update.type) {
            case PostUpdateType.ADD: {
              const addUpdate = update as any;
              const index = addUpdate.index ?? newPosts.length;
              newPosts.splice(index, 0, addUpdate.post as T);
              addedCount++;
              break;
            }

            case PostUpdateType.BATCH_ADD: {
              const batchAddUpdate = update as any;
              const postsToAdd = batchAddUpdate.posts || [];
              const startIndex = batchAddUpdate.startIndex ?? newPosts.length;
              newPosts.splice(startIndex, 0, ...postsToAdd as T[]);
              addedCount += postsToAdd.length;
              break;
            }

            case PostUpdateType.UPDATE: {
              const updatePost = update as any;
              const index = newPosts.findIndex(p => p.id === updatePost.postId);
              if (index !== -1) {
                newPosts[index] = { ...newPosts[index], ...updatePost.updates };
                updatedCount++;
              }
              break;
            }

            case PostUpdateType.BATCH_UPDATE: {
              const batchUpdate = update as any;
              const updatesList = batchUpdate.updates || [];
              for (const { postId, changes } of updatesList) {
                const index = newPosts.findIndex(p => p.id === postId);
                if (index !== -1) {
                  newPosts[index] = { ...newPosts[index], ...changes };
                  updatedCount++;
                }
              }
              break;
            }

            case PostUpdateType.DELETE: {
              const deleteUpdate = update as any;
              const prevLength = newPosts.length;
              newPosts = newPosts.filter(p => p.id !== deleteUpdate.postId);
              if (newPosts.length < prevLength) deletedCount++;
              break;
            }

            case PostUpdateType.BATCH_DELETE: {
              const batchDelete = update as any;
              const idsToDelete = new Set(batchDelete.postIds || []);
              const prevLength = newPosts.length;
              newPosts = newPosts.filter(p => !idsToDelete.has(p.id));
              deletedCount += prevLength - newPosts.length;
              break;
            }

            case PostUpdateType.MOVE: {
              const moveUpdate = update as any;
              const fromIndex = newPosts.findIndex(p => p.id === moveUpdate.postId);
              if (fromIndex !== -1) {
                const [movedPost] = newPosts.splice(fromIndex, 1);
                newPosts.splice(moveUpdate.toIndex, 0, movedPost);
              }
              break;
            }

            case PostUpdateType.RESET: {
              const resetUpdate = update as any;
              newPosts = resetUpdate.posts || [];
              break;
            }
          }
        }

        if (addedCount > 0 || updatedCount > 0 || deletedCount > 0) {
          setDiffUpdates(prev => ({
            addedCount: prev.addedCount + addedCount,
            updatedCount: prev.updatedCount + updatedCount,
            deletedCount: prev.deletedCount + deletedCount,
            lastUpdateTime: Date.now(),
          }));
        }

        return newPosts;
      });
    } finally {
      setIsProcessing(false);
      setPendingUpdateCount(batcherRef.current?.getPendingCount() ?? 0);
    }
  }, []);

  useEffect(() => {
    if (enableDiff) {
      calculatorRef.current = createPostDiffCalculator<T>(diffConfig);
    }
    return () => { calculatorRef.current = null; };
  }, [enableDiff, diffConfig]);

  useEffect(() => {
    if (!enableBatching) return;
    batcherRef.current = createPostUpdateBatcher(batcherOptions);
    const unsubscribe = batcherRef.current.subscribe(handleBatchUpdates);
    return () => {
      unsubscribe();
      batcherRef.current?.destroy();
      batcherRef.current = null;
    };
  }, [enableBatching, batcherOptions, handleBatchUpdates]);

  const syncFromStore = useCallback((state: PostsState) => {
    setLoading(state.isLoading);
    setRefreshing(state.isRefreshing);
    setError(state.error);
    setHasMore(state.hasMore);

    batcherRef.current?.clearPending();
    calculatorRef.current?.reset();
    if (state.posts && state.posts.length > 0) {
      const next = state.posts as unknown as T[];
      if (next.length > maxPostCount) {
        console.warn(`[useDifferentialPosts] Post count (${next.length}) exceeds limit (${maxPostCount})`);
      }
      setPosts(next);
      previousPostsRef.current = next;
    } else {
      setPosts([]);
      previousPostsRef.current = [];
    }
  }, [maxPostCount]);

  useEffect(() => {
    if (!autoSubscribe) return;

    const currentState = usePostListStore.getState().getPostsState(listKey);
    syncFromStore(currentState);

    const unsubscribe = usePostListStore.subscribe(state => {
      const postsState = state.postsStateMap.get(listKey);
      if (postsState) syncFromStore(postsState);
    });

    return unsubscribe;
  }, [autoSubscribe, listKey, syncFromStore]);

  const refresh = useCallback(async () => {
    setRefreshing(true);
    setError(null);
    try {
      await postSyncService.refreshPosts(listKey);
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : '刷新失败';
      setError(errorMsg);
    } finally {
      setRefreshing(false);
    }
  }, [listKey]);

  const loadMore = useCallback(async () => {
    if (hasMore && !loading) {
      setLoading(true);
      setError(null);
      try {
        await postSyncService.loadMorePosts(listKey);
      } catch (err) {
        const errorMsg = err instanceof Error ? err.message : '加载更多失败';
        setError(errorMsg);
      } finally {
        setLoading(false);
      }
    }
  }, [listKey, hasMore, loading]);

  const flush = useCallback(() => { batcherRef.current?.flush(); }, []);

  const reset = useCallback(() => {
    calculatorRef.current?.reset();
    batcherRef.current?.clearPending();
    setPosts([]);
    setLoading(false);
    setRefreshing(false);
    setError(null);
    setHasMore(true);
    previousPostsRef.current = [];
    setDiffUpdates({ addedCount: 0, updatedCount: 0, deletedCount: 0, lastUpdateTime: 0 });
  }, []);

  const forceUpdate = useCallback((newPosts: T[]) => {
    setPosts(newPosts);
    previousPostsRef.current = newPosts;
  }, []);

  const getDiffStats = useCallback(() => {
    const batcherStats = batcherRef.current?.getStats();
    return {
      totalBatches: batcherStats?.totalBatches ?? 0,
      totalUpdates: batcherStats?.totalUpdates ?? 0,
      averageBatchSize: batcherStats?.averageBatchSize ?? 0,
    };
  }, []);

  useEffect(() => {
    if (!enableBatching || !batcherRef.current) return;
    const interval = setInterval(() => {
      setPendingUpdateCount(batcherRef.current?.getPendingCount() ?? 0);
    }, 50);
    return () => clearInterval(interval);
  }, [enableBatching]);

  return {
    posts,
    loading,
    isInitialLoading: loading && posts.length === 0 && !refreshing,
    isLoadingMore: loading && posts.length > 0 && !refreshing,
    refreshing,
    error,
    hasMore,
    pendingUpdateCount,
    isProcessing,
    refresh,
    loadMore,
    flush,
    reset,
    forceUpdate,
    getDiffStats,
    diffUpdates,
  };
}

export default useDifferentialPosts;
