import { Injectable } from '@nestjs/common';
import {
  DirectMessage,
  FriendRequest,
  FriendRequestStatus,
  Friendship,
  PlayerTravelUnlock,
  SocialNotification,
  UserBlock,
  UserReport,
} from './social.entities';
import { SocialStore } from './social.store';

@Injectable()
export class SocialMemoryStore implements SocialStore {
  private nextId = BigInt(1);
  private friendships: Friendship[] = [];
  private requests: FriendRequest[] = [];
  private blocks: UserBlock[] = [];
  private messages: DirectMessage[] = [];
  private reports: UserReport[] = [];
  private unlocks: PlayerTravelUnlock[] = [];
  private notifications: SocialNotification[] = [];

  private id(): bigint { return this.nextId++; }

  async listFriendships(userId: bigint): Promise<Friendship[]> {
    return this.friendships.filter((item) => item.user_low_id === userId || item.user_high_id === userId);
  }
  async findFriendship(userLowId: bigint, userHighId: bigint): Promise<Friendship | null> {
    return this.friendships.find((item) => item.user_low_id === userLowId && item.user_high_id === userHighId) || null;
  }
  async createFriendship(userLowId: bigint, userHighId: bigint): Promise<Friendship> {
    const found = await this.findFriendship(userLowId, userHighId);
    if (found) return found;
    const record = Object.assign(new Friendship(), { id: this.id(), user_low_id: userLowId, user_high_id: userHighId, created_at: new Date() });
    this.friendships.push(record); return record;
  }
  async deleteFriendship(userLowId: bigint, userHighId: bigint): Promise<void> {
    this.friendships = this.friendships.filter((item) => item.user_low_id !== userLowId || item.user_high_id !== userHighId);
  }
  async findPendingFriendRequest(requesterId: bigint, recipientId: bigint): Promise<FriendRequest | null> {
    return this.requests.find((item) => item.requester_id === requesterId && item.recipient_id === recipientId && item.status === FriendRequestStatus.PENDING && item.expires_at > new Date()) || null;
  }
  async createFriendRequest(requesterId: bigint, recipientId: bigint, expiresAt: Date): Promise<FriendRequest> {
    const record = Object.assign(new FriendRequest(), { id: this.id(), requester_id: requesterId, recipient_id: recipientId, status: FriendRequestStatus.PENDING, created_at: new Date(), expires_at: expiresAt, responded_at: null });
    this.requests.push(record); return record;
  }
  async findFriendRequest(id: bigint): Promise<FriendRequest | null> { return this.requests.find((item) => item.id === id) || null; }
  async saveFriendRequest(request: FriendRequest): Promise<FriendRequest> { return request; }
  async cancelPendingRequestsBetween(userA: bigint, userB: bigint): Promise<void> {
    for (const request of this.requests) if (request.status === FriendRequestStatus.PENDING && ((request.requester_id === userA && request.recipient_id === userB) || (request.requester_id === userB && request.recipient_id === userA))) { request.status = FriendRequestStatus.CANCELLED; request.responded_at = new Date(); }
  }
  async listFriendRequests(userId: bigint): Promise<FriendRequest[]> { return this.requests.filter((item) => item.recipient_id === userId && item.status === FriendRequestStatus.PENDING && item.expires_at > new Date()).sort((a, b) => b.created_at.getTime() - a.created_at.getTime()); }
  async createBlock(userId: bigint, blockedUserId: bigint): Promise<UserBlock> {
    const found = this.blocks.find((item) => item.user_id === userId && item.blocked_user_id === blockedUserId); if (found) return found;
    const record = Object.assign(new UserBlock(), { id: this.id(), user_id: userId, blocked_user_id: blockedUserId, created_at: new Date() }); this.blocks.push(record); return record;
  }
  async deleteBlock(userId: bigint, blockedUserId: bigint): Promise<void> { this.blocks = this.blocks.filter((item) => item.user_id !== userId || item.blocked_user_id !== blockedUserId); }
  async isBlocked(userId: bigint, blockedUserId: bigint): Promise<boolean> { return this.blocks.some((item) => item.user_id === userId && item.blocked_user_id === blockedUserId); }
  async listBlocks(userId: bigint): Promise<UserBlock[]> { return this.blocks.filter((item) => item.user_id === userId); }
  async createDirectMessage(input: Pick<DirectMessage, 'sender_id' | 'recipient_id' | 'content' | 'expires_at'>): Promise<DirectMessage> {
    const record = Object.assign(new DirectMessage(), { id: this.id(), ...input, created_at: new Date(), read_at: null }); this.messages.push(record); return record;
  }
  async listDirectMessages(userA: bigint, userB: bigint, limit: number, before?: Date): Promise<DirectMessage[]> { return this.messages.filter((item) => ((item.sender_id === userA && item.recipient_id === userB) || (item.sender_id === userB && item.recipient_id === userA)) && item.expires_at > new Date() && (!before || item.created_at < before)).sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit); }
  async markDirectMessagesRead(readerId: bigint, otherUserId: bigint): Promise<number> { let affected = 0; for (const item of this.messages) if (item.sender_id === otherUserId && item.recipient_id === readerId && !item.read_at) { item.read_at = new Date(); affected++; } return affected; }
  async listConversations(userId: bigint): Promise<DirectMessage[]> { const latest = new Map<string, DirectMessage>(); for (const item of this.messages) { if (item.expires_at <= new Date() || (item.sender_id !== userId && item.recipient_id !== userId)) continue; const other = item.sender_id === userId ? item.recipient_id : item.sender_id; const old = latest.get(other.toString()); if (!old || old.created_at < item.created_at) latest.set(other.toString(), item); } return [...latest.values()].sort((a, b) => b.created_at.getTime() - a.created_at.getTime()); }
  async countUnreadDirectMessages(userId: bigint): Promise<number> { return this.messages.filter((item) => item.recipient_id === userId && !item.read_at && item.expires_at > new Date()).length; }
  async createReport(input: Pick<UserReport, 'reporter_id' | 'reported_user_id' | 'reason' | 'note' | 'message_id'>): Promise<UserReport> { const record = Object.assign(new UserReport(), { id: this.id(), ...input, status: 'received', created_at: new Date() }); this.reports.push(record); return record; }
  async createUnlock(userId: bigint, destinationId: string): Promise<PlayerTravelUnlock> { const found = this.unlocks.find((item) => item.user_id === userId && item.destination_id === destinationId); if (found) return found; const record = Object.assign(new PlayerTravelUnlock(), { id: this.id(), user_id: userId, destination_id: destinationId, discovered_at: new Date() }); this.unlocks.push(record); return record; }
  async listUnlocks(userId: bigint): Promise<PlayerTravelUnlock[]> { return this.unlocks.filter((item) => item.user_id === userId); }
  async createNotification(input: Pick<SocialNotification, 'user_id' | 'category' | 'title' | 'content' | 'action_metadata' | 'expires_at'>): Promise<SocialNotification> { const now = new Date(); const record = Object.assign(new SocialNotification(), { id: this.id(), ...input, created_at: now, updated_at: now, read_at: null }); this.notifications.push(record); return record; }
  async listNotifications(userId: bigint, limit: number, before?: Date): Promise<SocialNotification[]> { return this.notifications.filter((item) => item.user_id === userId && item.expires_at > new Date() && (!before || item.created_at < before)).sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit); }
  async markNotificationRead(userId: bigint, id: bigint): Promise<SocialNotification | null> { const item = this.notifications.find((entry) => entry.user_id === userId && entry.id === id); if (item) { item.read_at = new Date(); item.updated_at = new Date(); } return item || null; }
  async markAllNotificationsRead(userId: bigint): Promise<number> { let affected = 0; for (const item of this.notifications) if (item.user_id === userId && !item.read_at) { item.read_at = new Date(); item.updated_at = new Date(); affected++; } return affected; }
  async countUnreadNotifications(userId: bigint): Promise<number> { return this.notifications.filter((item) => item.user_id === userId && !item.read_at && item.expires_at > new Date()).length; }
  async purgeUsers(userIds: bigint[]): Promise<void> {
    const ids = new Set(userIds.map((id) => id.toString()));
    const has = (id: bigint) => ids.has(id.toString());
    this.friendships = this.friendships.filter((item) => !has(item.user_low_id) && !has(item.user_high_id));
    this.requests = this.requests.filter((item) => !has(item.requester_id) && !has(item.recipient_id));
    this.blocks = this.blocks.filter((item) => !has(item.user_id) && !has(item.blocked_user_id));
    this.messages = this.messages.filter((item) => !has(item.sender_id) && !has(item.recipient_id));
    this.reports = this.reports.filter((item) => !has(item.reporter_id) && !has(item.reported_user_id));
    this.unlocks = this.unlocks.filter((item) => !has(item.user_id));
    this.notifications = this.notifications.filter((item) => !has(item.user_id) && !(item.action_metadata as Record<string, unknown> | null)?.testLab);
  }
  async cleanupExpired(now: Date): Promise<void> { this.requests = this.requests.filter((item) => item.status !== FriendRequestStatus.PENDING || item.expires_at > now); this.messages = this.messages.filter((item) => item.expires_at > now); this.notifications = this.notifications.filter((item) => item.expires_at > now); }
}
