import { Post } from '../types';
import { postService } from '../services/postService';
import { CacheBus, CacheEvent } from './cacheBus';

interface PostListPayload {
  key: string;
  posts: Post[];
}

interface PostDetailPayload {
  postId: string;
  post: Post | null;
}

interface PostManagerSnapshot {
  listKeys: string[];
  detailKeys: string[];
}

type PostManagerEvent =
  | CacheEvent<PostListPayload>
  | CacheEvent<PostDetailPayload>
  | CacheEvent<PostManagerSnapshot>
  | CacheEvent<{ error: unknown; context: string }>;

const LIST_TTL = 30 * 1000;
const DETAIL_TTL = 60 * 1000;

interface CacheEntry<T> {
  data: T;
  timestamp: number;
  ttl: number;
}

class PostManager extends CacheBus<PostManagerSnapshot, PostManagerEvent> {
  private listCache = new Map<string, CacheEntry<Post[]>>();
  private detailCache = new Map<string, CacheEntry<Post | null>>();
  private pendingRequests = new Map<string, Promise<any>>();

  protected getSnapshot(): PostManagerSnapshot {
    return {
      listKeys: [...this.listCache.keys()],
      detailKeys: [...this.detailCache.keys()],
    };
  }

  private isExpired(entry: CacheEntry<any>): boolean {
    return Date.now() - entry.timestamp > entry.ttl;
  }

  private async dedupe<T>(key: string, fetcher: () => Promise<T>): Promise<T> {
    const pending = this.pendingRequests.get(key) as Promise<T> | undefined;
    if (pending) return pending;
    const request = fetcher().finally(() => {
      this.pendingRequests.delete(key);
    });
    this.pendingRequests.set(key, request);
    return request;
  }

  private listKey(type: string, page: number, pageSize: number): string {
    return `${type}:${page}:${pageSize}`;
  }

  async getPosts(
    type = 'hot',
    page = 1,
    pageSize = 20,
    forceRefresh = false
  ): Promise<Post[]> {
    const key = this.listKey(type, page, pageSize);
    const cached = this.listCache.get(key);
    if (!forceRefresh && cached && !this.isExpired(cached)) {
      return cached.data;
    }

    if (!forceRefresh && cached && this.isExpired(cached)) {
      this.refreshPostsInBackground(key, type, page, pageSize);
      return cached.data;
    }

    return this.dedupe(`posts:list:${key}`, async () => {
      const response = await postService.getPosts(page, pageSize, type);
      const posts = response.list || [];
      this.listCache.set(key, { data: posts, timestamp: Date.now(), ttl: LIST_TTL });
      this.notify({
        type: 'list_updated',
        payload: { key, posts },
        timestamp: Date.now(),
      });
      return posts;
    });
  }

  private refreshPostsInBackground(key: string, type: string, page: number, pageSize: number): void {
    this.dedupe(`posts:list:bg:${key}`, async () => {
      const response = await postService.getPosts(page, pageSize, type);
      const posts = response.list || [];
      this.listCache.set(key, { data: posts, timestamp: Date.now(), ttl: LIST_TTL });
      this.notify({
        type: 'list_updated',
        payload: { key, posts },
        timestamp: Date.now(),
      });
      return posts;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshPostsInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  async getPostDetail(postId: string, forceRefresh = false): Promise<Post | null> {
    const cached = this.detailCache.get(postId);
    if (!forceRefresh && cached && !this.isExpired(cached)) {
      return cached.data;
    }

    if (!forceRefresh && cached && this.isExpired(cached)) {
      this.refreshPostDetailInBackground(postId);
      return cached.data;
    }

    return this.dedupe(`posts:detail:${postId}`, async () => {
      const post = await postService.getPost(postId);
      this.detailCache.set(postId, { data: post, timestamp: Date.now(), ttl: DETAIL_TTL });
      this.notify({
        type: 'detail_updated',
        payload: { postId, post },
        timestamp: Date.now(),
      });
      return post;
    });
  }

  private refreshPostDetailInBackground(postId: string): void {
    this.dedupe(`posts:detail:bg:${postId}`, async () => {
      const post = await postService.getPost(postId);
      this.detailCache.set(postId, { data: post, timestamp: Date.now(), ttl: DETAIL_TTL });
      this.notify({
        type: 'detail_updated',
        payload: { postId, post },
        timestamp: Date.now(),
      });
      return post;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshPostDetailInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  invalidate(postId?: string): void {
    if (postId) {
      this.detailCache.delete(postId);
      return;
    }
    this.listCache.clear();
    this.detailCache.clear();
  }

  clear(): void {
    this.listCache.clear();
    this.detailCache.clear();
    this.pendingRequests.clear();
  }
}

export const postManager = new PostManager();

