import {
  GroupListResponse,
  GroupMemberListResponse,
  GroupMemberResponse,
  GroupResponse,
} from '../types/dto';
import { groupService } from '../services/groupService';
import {
  getAllGroupsCache,
  getGroupCache,
  getGroupMembersCache,
  saveGroupCache,
  saveGroupMembersCache,
  saveGroupsCache,
  saveUsersCache,
} from '../services/database';
import { CacheBus, CacheEvent } from './cacheBus';

interface GroupManagerSnapshot {
  groupIds: string[];
  memberGroupIds: string[];
}

type GroupManagerEvent =
  | CacheEvent<{ groups: GroupResponse[] }>
  | CacheEvent<{ groupId: string; group: GroupResponse | null }>
  | CacheEvent<{ groupId: string; members: GroupMemberResponse[] }>
  | CacheEvent<GroupManagerSnapshot>
  | CacheEvent<{ error: unknown; context: string }>;

const GROUP_TTL = 60 * 1000;
const MEMBERS_TTL = 120 * 1000;

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

class GroupManager extends CacheBus<GroupManagerSnapshot, GroupManagerEvent> {
  private groupsListCache: CacheEntry<GroupResponse[]> | null = null;
  private groupDetailCache = new Map<string, CacheEntry<GroupResponse | null>>();
  private groupMembersCache = new Map<string, CacheEntry<GroupMemberResponse[]>>();
  private pendingRequests = new Map<string, Promise<any>>();

  protected getSnapshot(): GroupManagerSnapshot {
    return {
      groupIds: [...this.groupDetailCache.keys()],
      memberGroupIds: [...this.groupMembersCache.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;
  }

  async getGroups(page = 1, pageSize = 20, forceRefresh = false): Promise<GroupListResponse> {
    if (page === 1 && !forceRefresh && this.groupsListCache && !this.isExpired(this.groupsListCache)) {
      return {
        list: this.groupsListCache.data,
        total: this.groupsListCache.data.length,
        page: 1,
        page_size: pageSize,
        total_pages: 1,
      };
    }

    if (page === 1 && !forceRefresh && this.groupsListCache && this.isExpired(this.groupsListCache)) {
      this.refreshGroupsInBackground(page, pageSize);
      return {
        list: this.groupsListCache.data,
        total: this.groupsListCache.data.length,
        page: 1,
        page_size: pageSize,
        total_pages: 1,
      };
    }

    if (page === 1 && !forceRefresh) {
      const localGroups = await getAllGroupsCache();
      if (localGroups.length > 0) {
        this.groupsListCache = { data: localGroups, timestamp: Date.now(), ttl: GROUP_TTL };
        this.notify({
          type: 'list_updated',
          payload: { groups: localGroups },
          timestamp: Date.now(),
        });
        this.refreshGroupsInBackground(page, pageSize);
        return {
          list: localGroups,
          total: localGroups.length,
          page: 1,
          page_size: pageSize,
          total_pages: 1,
        };
      }
    }

    return this.dedupe(`groups:list:${page}:${pageSize}`, async () => {
      const response = await groupService.fetchGroupsFromApi(page, pageSize);
      const groups = response.list || [];
      if (page === 1) {
        this.groupsListCache = { data: groups, timestamp: Date.now(), ttl: GROUP_TTL };
      }
      await saveGroupsCache(groups).catch(() => {});
      this.notify({
        type: 'list_updated',
        payload: { groups },
        timestamp: Date.now(),
      });
      return response;
    });
  }

  private refreshGroupsInBackground(page = 1, pageSize = 20): void {
    this.dedupe(`groups:list:bg:${page}:${pageSize}`, async () => {
      const response = await groupService.fetchGroupsFromApi(page, pageSize);
      const groups = response.list || [];
      if (page === 1) {
        this.groupsListCache = { data: groups, timestamp: Date.now(), ttl: GROUP_TTL };
        saveGroupsCache(groups).catch(() => {});
      }
      this.notify({
        type: 'list_updated',
        payload: { groups },
        timestamp: Date.now(),
      });
      return response;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshGroupsInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  async getGroup(groupId: string, forceRefresh = false): Promise<GroupResponse> {
    const id = groupId;
    const cached = this.groupDetailCache.get(id);
    if (!forceRefresh && cached && !this.isExpired(cached) && cached.data) {
      return cached.data;
    }

    if (!forceRefresh && cached && this.isExpired(cached) && cached.data) {
      this.refreshGroupInBackground(id);
      return cached.data;
    }

    if (!forceRefresh) {
      const localGroup = await getGroupCache(id);
      if (localGroup) {
        this.groupDetailCache.set(id, { data: localGroup, timestamp: Date.now(), ttl: GROUP_TTL });
        this.notify({
          type: 'detail_updated',
          payload: { groupId: id, group: localGroup },
          timestamp: Date.now(),
        });
        this.refreshGroupInBackground(id);
        return localGroup;
      }
    }

    return this.dedupe(`groups:detail:${id}`, async () => {
      const group = await groupService.fetchGroupFromApi(id);
      this.groupDetailCache.set(id, { data: group, timestamp: Date.now(), ttl: GROUP_TTL });
      await saveGroupCache(group).catch(() => {});
      this.notify({
        type: 'detail_updated',
        payload: { groupId: id, group },
        timestamp: Date.now(),
      });
      return group;
    });
  }

  private refreshGroupInBackground(groupId: string): void {
    this.dedupe(`groups:detail:bg:${groupId}`, async () => {
      const group = await groupService.fetchGroupFromApi(groupId);
      this.groupDetailCache.set(groupId, { data: group, timestamp: Date.now(), ttl: GROUP_TTL });
      saveGroupCache(group).catch(() => {});
      this.notify({
        type: 'detail_updated',
        payload: { groupId, group },
        timestamp: Date.now(),
      });
      return group;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshGroupInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  async getMembers(groupId: string, page = 1, pageSize = 50, forceRefresh = false): Promise<GroupMemberListResponse> {
    const id = groupId;
    const key = `${id}:${page}:${pageSize}`;
    const cached = this.groupMembersCache.get(key);
    if (!forceRefresh && cached && !this.isExpired(cached)) {
      return {
        list: cached.data,
        total: cached.data.length,
        page,
        page_size: pageSize,
        total_pages: 1,
      };
    }

    if (!forceRefresh && cached && this.isExpired(cached)) {
      this.refreshMembersInBackground(id, page, pageSize);
      return {
        list: cached.data,
        total: cached.data.length,
        page,
        page_size: pageSize,
        total_pages: 1,
      };
    }

    if (page === 1 && !forceRefresh) {
      const localMembers = await getGroupMembersCache(id);
      if (localMembers.length > 0) {
        this.groupMembersCache.set(key, {
          data: localMembers,
          timestamp: Date.now(),
          ttl: MEMBERS_TTL,
        });
        this.notify({
          type: 'detail_updated',
          payload: { groupId: id, members: localMembers },
          timestamp: Date.now(),
        });
        this.refreshMembersInBackground(id, page, pageSize);
        return {
          list: localMembers,
          total: localMembers.length,
          page,
          page_size: pageSize,
          total_pages: 1,
        };
      }
    }

    return this.dedupe(`groups:members:${key}`, async () => {
      const response = await groupService.fetchMembersFromApi(id, page, pageSize);
      const members = response.list || [];
      this.groupMembersCache.set(key, { data: members, timestamp: Date.now(), ttl: MEMBERS_TTL });
      if (page === 1) {
        await saveGroupMembersCache(id, members).catch(() => {});
      }
      await saveUsersCache(
        members
          .map((member) => member.user)
          .filter((user): user is NonNullable<typeof user> => Boolean(user))
      ).catch(() => {});
      this.notify({
        type: 'detail_updated',
        payload: { groupId: id, members },
        timestamp: Date.now(),
      });
      return response;
    });
  }

  private refreshMembersInBackground(groupId: string, page = 1, pageSize = 50): void {
    this.dedupe(`groups:members:bg:${groupId}:${page}:${pageSize}`, async () => {
      const response = await groupService.fetchMembersFromApi(groupId, page, pageSize);
      const members = response.list || [];
      const key = `${groupId}:${page}:${pageSize}`;
      this.groupMembersCache.set(key, { data: members, timestamp: Date.now(), ttl: MEMBERS_TTL });
      if (page === 1) {
        saveGroupMembersCache(groupId, members).catch(() => {});
      }
      saveUsersCache(
        members
          .map((member) => member.user)
          .filter((user): user is NonNullable<typeof user> => Boolean(user))
      ).catch(() => {});
      this.notify({
        type: 'detail_updated',
        payload: { groupId, members },
        timestamp: Date.now(),
      });
      return response;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshMembersInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  invalidate(groupId?: string): void {
    if (!groupId) {
      this.groupsListCache = null;
      this.groupDetailCache.clear();
      this.groupMembersCache.clear();
      return;
    }
    this.groupDetailCache.delete(groupId);
    [...this.groupMembersCache.keys()].forEach((key) => {
      if (key.startsWith(`${groupId}:`)) {
        this.groupMembersCache.delete(key);
      }
    });
  }
}

export const groupManager = new GroupManager();

