import { UserDTO } from '../types/dto';
import { authService } from '../services/authService';
import {
  getCurrentUserCache,
  getUserCache,
  saveCurrentUserCache,
  saveUserCache,
} from '../services/database';
import { CacheBus, CacheEvent } from './cacheBus';

interface UserManagerSnapshot {
  hasCurrentUser: boolean;
  cachedUserIds: string[];
}

type UserManagerEvent =
  | CacheEvent<{ user: UserDTO | null }>
  | CacheEvent<{ userId: string; user: UserDTO | null }>
  | CacheEvent<UserManagerSnapshot>
  | CacheEvent<{ error: unknown; context: string }>;

const USER_TTL = 5 * 60 * 1000;

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

class UserManager extends CacheBus<UserManagerSnapshot, UserManagerEvent> {
  private currentUserCache: CacheEntry<UserDTO | null> | null = null;
  private userCache = new Map<string, CacheEntry<UserDTO | null>>();
  private pendingRequests = new Map<string, Promise<any>>();

  protected getSnapshot(): UserManagerSnapshot {
    return {
      hasCurrentUser: !!this.currentUserCache?.data,
      cachedUserIds: [...this.userCache.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 getCurrentUser(forceRefresh = false): Promise<UserDTO | null> {
    if (!forceRefresh && this.currentUserCache && !this.isExpired(this.currentUserCache)) {
      return this.currentUserCache.data;
    }

    if (!forceRefresh && this.currentUserCache && this.isExpired(this.currentUserCache)) {
      this.refreshCurrentUserInBackground();
      return this.currentUserCache.data;
    }

    if (!forceRefresh) {
      const cachedFromDb = await getCurrentUserCache();
      if (cachedFromDb) {
        this.currentUserCache = { data: cachedFromDb, timestamp: Date.now(), ttl: USER_TTL };
        this.notify({
          type: 'detail_updated',
          payload: { user: cachedFromDb },
          timestamp: Date.now(),
        });
        this.refreshCurrentUserInBackground();
        return cachedFromDb;
      }
    }

    return this.dedupe('users:current', async () => {
      const user = await authService.fetchCurrentUserFromAPI();
      this.currentUserCache = { data: user, timestamp: Date.now(), ttl: USER_TTL };
      if (user) {
        saveCurrentUserCache(user).catch(() => {});
        saveUserCache(user).catch(() => {});
      }
      this.notify({
        type: 'detail_updated',
        payload: { user },
        timestamp: Date.now(),
      });
      return user;
    });
  }

  private refreshCurrentUserInBackground(): void {
    this.dedupe('users:current:bg', async () => {
      const user = await authService.fetchCurrentUserFromAPI();
      this.currentUserCache = { data: user, timestamp: Date.now(), ttl: USER_TTL };
      if (user) {
        saveCurrentUserCache(user).catch(() => {});
        saveUserCache(user).catch(() => {});
      }
      this.notify({
        type: 'detail_updated',
        payload: { user },
        timestamp: Date.now(),
      });
      return user;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshCurrentUserInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  async getUserById(userId: string, forceRefresh = false): Promise<UserDTO | null> {
    const cached = this.userCache.get(userId);
    if (!forceRefresh && cached && !this.isExpired(cached)) {
      return cached.data;
    }

    if (!forceRefresh && cached && this.isExpired(cached)) {
      this.refreshUserByIdInBackground(userId);
      return cached.data;
    }

    if (!forceRefresh) {
      const cachedFromDb = await getUserCache(userId);
      if (cachedFromDb) {
        this.userCache.set(userId, { data: cachedFromDb, timestamp: Date.now(), ttl: USER_TTL });
        this.notify({
          type: 'detail_updated',
          payload: { userId, user: cachedFromDb },
          timestamp: Date.now(),
        });
        this.refreshUserByIdInBackground(userId);
        return cachedFromDb;
      }
    }

    return this.dedupe(`users:detail:${userId}`, async () => {
      const user = await authService.fetchUserByIdFromAPI(userId);
      this.userCache.set(userId, { data: user, timestamp: Date.now(), ttl: USER_TTL });
      if (user) {
        saveUserCache(user).catch(() => {});
      }
      this.notify({
        type: 'detail_updated',
        payload: { userId, user },
        timestamp: Date.now(),
      });
      return user;
    });
  }

  private refreshUserByIdInBackground(userId: string): void {
    this.dedupe(`users:detail:bg:${userId}`, async () => {
      const user = await authService.fetchUserByIdFromAPI(userId);
      this.userCache.set(userId, { data: user, timestamp: Date.now(), ttl: USER_TTL });
      if (user) {
        saveUserCache(user).catch(() => {});
      }
      this.notify({
        type: 'detail_updated',
        payload: { userId, user },
        timestamp: Date.now(),
      });
      return user;
    }).catch((error) => {
      this.notify({
        type: 'error',
        payload: { error, context: 'refreshUserByIdInBackground' },
        timestamp: Date.now(),
      });
    });
  }

  invalidateUsers(): void {
    this.currentUserCache = null;
    this.userCache.clear();
  }
}

export const userManager = new UserManager();

