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

@Injectable()
export class SocialDatabaseStore implements SocialStore {
  constructor(
    @InjectRepository(Friendship) private readonly friendships: Repository<Friendship>,
    @InjectRepository(FriendRequest) private readonly requests: Repository<FriendRequest>,
    @InjectRepository(UserBlock) private readonly blocks: Repository<UserBlock>,
    @InjectRepository(DirectMessage) private readonly messages: Repository<DirectMessage>,
    @InjectRepository(UserReport) private readonly reports: Repository<UserReport>,
    @InjectRepository(PlayerTravelUnlock) private readonly unlocks: Repository<PlayerTravelUnlock>,
    @InjectRepository(SocialNotification) private readonly notifications: Repository<SocialNotification>,
  ) {}

  async listFriendships(userId: bigint): Promise<Friendship[]> {
    return this.friendships.find({ where: [{ user_low_id: userId }, { user_high_id: userId }], order: { created_at: 'DESC' } });
  }
  async findFriendship(userLowId: bigint, userHighId: bigint): Promise<Friendship | null> { return this.friendships.findOne({ where: { user_low_id: userLowId, user_high_id: userHighId } }); }
  async createFriendship(userLowId: bigint, userHighId: bigint): Promise<Friendship> {
    const existing = await this.findFriendship(userLowId, userHighId); return existing || this.friendships.save(this.friendships.create({ user_low_id: userLowId, user_high_id: userHighId }));
  }
  async deleteFriendship(userLowId: bigint, userHighId: bigint): Promise<void> { await this.friendships.delete({ user_low_id: userLowId, user_high_id: userHighId }); }
  async findPendingFriendRequest(requesterId: bigint, recipientId: bigint): Promise<FriendRequest | null> { return this.requests.findOne({ where: { requester_id: requesterId, recipient_id: recipientId, status: FriendRequestStatus.PENDING, expires_at: MoreThan(new Date()) } }); }
  async createFriendRequest(requesterId: bigint, recipientId: bigint, expiresAt: Date): Promise<FriendRequest> { return this.requests.save(this.requests.create({ requester_id: requesterId, recipient_id: recipientId, expires_at: expiresAt, status: FriendRequestStatus.PENDING })); }
  async findFriendRequest(id: bigint): Promise<FriendRequest | null> { return this.requests.findOne({ where: { id } }); }
  async saveFriendRequest(request: FriendRequest): Promise<FriendRequest> { return this.requests.save(request); }
  async cancelPendingRequestsBetween(userA: bigint, userB: bigint): Promise<void> {
    await this.requests.createQueryBuilder().update(FriendRequest).set({ status: FriendRequestStatus.CANCELLED, responded_at: new Date() }).where('status = :status', { status: FriendRequestStatus.PENDING }).andWhere(new Brackets((qb) => qb.where('(requester_id = :a AND recipient_id = :b)', { a: userA, b: userB }).orWhere('(requester_id = :b AND recipient_id = :a)', { a: userA, b: userB }))).execute();
  }
  async listFriendRequests(userId: bigint): Promise<FriendRequest[]> { return this.requests.find({ where: { recipient_id: userId, status: FriendRequestStatus.PENDING, expires_at: MoreThan(new Date()) }, order: { created_at: 'DESC' } }); }
  async createBlock(userId: bigint, blockedUserId: bigint): Promise<UserBlock> { const found = await this.blocks.findOne({ where: { user_id: userId, blocked_user_id: blockedUserId } }); return found || this.blocks.save(this.blocks.create({ user_id: userId, blocked_user_id: blockedUserId })); }
  async deleteBlock(userId: bigint, blockedUserId: bigint): Promise<void> { await this.blocks.delete({ user_id: userId, blocked_user_id: blockedUserId }); }
  async isBlocked(userId: bigint, blockedUserId: bigint): Promise<boolean> { return (await this.blocks.count({ where: { user_id: userId, blocked_user_id: blockedUserId } })) > 0; }
  async listBlocks(userId: bigint): Promise<UserBlock[]> { return this.blocks.find({ where: { user_id: userId }, order: { created_at: 'DESC' } }); }
  async createDirectMessage(input: Pick<DirectMessage, 'sender_id' | 'recipient_id' | 'content' | 'expires_at'>): Promise<DirectMessage> { return this.messages.save(this.messages.create(input)); }
  async listDirectMessages(userA: bigint, userB: bigint, limit: number, before?: Date): Promise<DirectMessage[]> { const query = this.messages.createQueryBuilder('message').where('message.expires_at > :now', { now: new Date() }).andWhere(new Brackets((qb) => qb.where('(message.sender_id = :a AND message.recipient_id = :b)', { a: userA, b: userB }).orWhere('(message.sender_id = :b AND message.recipient_id = :a)', { a: userA, b: userB }))); if (before) query.andWhere('message.created_at < :before', { before }); return query.orderBy('message.created_at', 'DESC').take(limit).getMany(); }
  async markDirectMessagesRead(readerId: bigint, otherUserId: bigint): Promise<number> { const result = await this.messages.createQueryBuilder().update(DirectMessage).set({ read_at: new Date() }).where('sender_id = :otherUserId AND recipient_id = :readerId AND read_at IS NULL', { readerId, otherUserId }).execute(); return result.affected || 0; }
  async listConversations(userId: bigint): Promise<DirectMessage[]> { const rows = await this.messages.createQueryBuilder('message').where('(message.sender_id = :userId OR message.recipient_id = :userId)', { userId }).andWhere('message.expires_at > :now', { now: new Date() }).orderBy('message.created_at', 'DESC').getMany(); const seen = new Set<string>(); return rows.filter((row) => { const other = (row.sender_id === userId ? row.recipient_id : row.sender_id).toString(); if (seen.has(other)) return false; seen.add(other); return true; }); }
  async countUnreadDirectMessages(userId: bigint): Promise<number> { return this.messages.count({ where: { recipient_id: userId, read_at: null, expires_at: MoreThan(new Date()) } }); }
  async createReport(input: Pick<UserReport, 'reporter_id' | 'reported_user_id' | 'reason' | 'note' | 'message_id'>): Promise<UserReport> { return this.reports.save(this.reports.create(input)); }
  async createUnlock(userId: bigint, destinationId: string): Promise<PlayerTravelUnlock> { const found = await this.unlocks.findOne({ where: { user_id: userId, destination_id: destinationId } }); return found || this.unlocks.save(this.unlocks.create({ user_id: userId, destination_id: destinationId })); }
  async listUnlocks(userId: bigint): Promise<PlayerTravelUnlock[]> { return this.unlocks.find({ where: { user_id: userId }, order: { discovered_at: 'ASC' } }); }
  async createNotification(input: Pick<SocialNotification, 'user_id' | 'category' | 'title' | 'content' | 'action_metadata' | 'expires_at'>): Promise<SocialNotification> { return this.notifications.save(this.notifications.create(input)); }
  async listNotifications(userId: bigint, limit: number, before?: Date): Promise<SocialNotification[]> { const where: any = { user_id: userId, expires_at: MoreThan(new Date()) }; if (before) where.created_at = LessThan(before); return this.notifications.find({ where, order: { created_at: 'DESC' }, take: limit }); }
  async markNotificationRead(userId: bigint, id: bigint): Promise<SocialNotification | null> { const notification = await this.notifications.findOne({ where: { id, user_id: userId } }); if (!notification) return null; notification.read_at = new Date(); return this.notifications.save(notification); }
  async markAllNotificationsRead(userId: bigint): Promise<number> { const result = await this.notifications.createQueryBuilder().update(SocialNotification).set({ read_at: new Date() }).where('user_id = :userId AND read_at IS NULL', { userId }).execute(); return result.affected || 0; }
  async countUnreadNotifications(userId: bigint): Promise<number> { return this.notifications.count({ where: { user_id: userId, read_at: null, expires_at: MoreThan(new Date()) } }); }
  async purgeUsers(userIds: bigint[]): Promise<void> {
    if (userIds.length === 0) return;
    const ids = userIds;
    await Promise.all([
      this.friendships.createQueryBuilder().delete().where('user_low_id IN (:...ids) OR user_high_id IN (:...ids)', { ids }).execute(),
      this.requests.createQueryBuilder().delete().where('requester_id IN (:...ids) OR recipient_id IN (:...ids)', { ids }).execute(),
      this.blocks.createQueryBuilder().delete().where('user_id IN (:...ids) OR blocked_user_id IN (:...ids)', { ids }).execute(),
      this.messages.createQueryBuilder().delete().where('sender_id IN (:...ids) OR recipient_id IN (:...ids)', { ids }).execute(),
      this.reports.createQueryBuilder().delete().where('reporter_id IN (:...ids) OR reported_user_id IN (:...ids)', { ids }).execute(),
      this.unlocks.createQueryBuilder().delete().where('user_id IN (:...ids)', { ids }).execute(),
      this.notifications.createQueryBuilder().delete().where('user_id IN (:...ids)', { ids }).orWhere("JSON_CONTAINS(action_metadata, JSON_OBJECT('testLab', true))").execute(),
    ]);
  }
  async cleanupExpired(now: Date): Promise<void> { await Promise.all([this.requests.delete({ status: FriendRequestStatus.PENDING, expires_at: LessThan(now) }), this.messages.delete({ expires_at: LessThan(now) }), this.notifications.delete({ expires_at: LessThan(now) })]); }
}
