import { Injectable, Inject } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import {
  NotificationRepository,
  NotificationWithReadStatus,
} from '../../../domain/repositories/notification.repository';
import {
  NotificationEntity,
  NotificationType,
  TargetType,
} from '../../../domain/entities/notification.entity';
import {
  NotificationMapper,
  NotificationWithTargets,
} from '../mappers/notification.mapper';
import {
  USER_TAG_REPOSITORY,
  UserTagRepository,
} from '../../../domain/repositories/user-tag.repository';

@Injectable()
export class NotificationRepositoryImpl implements NotificationRepository {
  constructor(
    private readonly prisma: PrismaService,
    private readonly mapper: NotificationMapper,
    @Inject(USER_TAG_REPOSITORY)
    private readonly tagRepository: UserTagRepository,
  ) {}

  async save(notification: NotificationEntity): Promise<NotificationEntity> {
    const data = this.mapper.toPersistence(notification);

    // 使用事务保存通知和关联数据
    const saved = await this.prisma.$transaction(async (tx) => {
      // 保存通知主体
      const result = await tx.notification.upsert({
        where: { id: notification.id },
        create: data,
        update: data,
      });

      // 删除旧的关联
      await tx.notificationTagTarget.deleteMany({
        where: { notificationId: notification.id },
      });
      await tx.notificationUserTarget.deleteMany({
        where: { notificationId: notification.id },
      });

      // 保存新的标签关联
      if (
        notification.targetType === TargetType.BY_TAG &&
        notification.targetConfig?.tagIds?.length
      ) {
        await tx.notificationTagTarget.createMany({
          data: notification.targetConfig.tagIds.map((tagId) => ({
            notificationId: notification.id,
            tagId,
          })),
        });
      }

      // 保存新的用户关联
      if (
        notification.targetType === TargetType.SPECIFIC &&
        notification.targetConfig?.accountSequences?.length
      ) {
        await tx.notificationUserTarget.createMany({
          data: notification.targetConfig.accountSequences.map(
            (accountSequence) => ({
              notificationId: notification.id,
              accountSequence,
            }),
          ),
        });
      }

      return result;
    });

    // 重新加载完整数据
    const complete = await this.prisma.notification.findUnique({
      where: { id: saved.id },
      include: {
        targetTags: { select: { tagId: true } },
        targetUsers: { select: { accountSequence: true } },
      },
    });

    return this.mapper.toDomain(complete as NotificationWithTargets);
  }

  async findById(id: string): Promise<NotificationEntity | null> {
    const notification = await this.prisma.notification.findUnique({
      where: { id },
      include: {
        targetTags: { select: { tagId: true } },
        targetUsers: { select: { accountSequence: true } },
      },
    });
    return notification
      ? this.mapper.toDomain(notification as NotificationWithTargets)
      : null;
  }

  async findActiveNotifications(params?: {
    type?: NotificationType;
    limit?: number;
    offset?: number;
  }): Promise<NotificationEntity[]> {
    const now = new Date();
    const notifications = await this.prisma.notification.findMany({
      where: {
        isEnabled: true,
        publishedAt: { lte: now },
        OR: [{ expiresAt: null }, { expiresAt: { gt: now } }],
        ...(params?.type && { type: params.type }),
      },
      include: {
        targetTags: { select: { tagId: true } },
        targetUsers: { select: { accountSequence: true } },
      },
      orderBy: [{ priority: 'desc' }, { publishedAt: 'desc' }],
      take: params?.limit ?? 50,
      skip: params?.offset ?? 0,
    });
    return notifications.map((n) =>
      this.mapper.toDomain(n as NotificationWithTargets),
    );
  }

  async findNotificationsForUser(params: {
    userSerialNum: string;
    type?: NotificationType;
    limit?: number;
    offset?: number;
  }): Promise<NotificationWithReadStatus[]> {
    const now = new Date();

    // 获取用户的标签
    const userTagIds = await this.tagRepository.findUserTagIds(
      params.userSerialNum,
    );

    // 查询通知
    const notifications = await this.prisma.notification.findMany({
      where: {
        isEnabled: true,
        ...(params.type && { type: params.type }),
        AND: [
          // publishedAt 为 null 表示立即发布，或者 publishedAt <= now
          { OR: [{ publishedAt: null }, { publishedAt: { lte: now } }] },
          // expiresAt 为 null 表示永不过期，或者 expiresAt > now
          { OR: [{ expiresAt: null }, { expiresAt: { gt: now } }] },
          // 目标用户筛选
          {
            OR: [
              // ALL: 发给所有人
              { targetType: 'ALL' },
              // BY_TAG: 用户必须有指定的标签
              {
                targetType: 'BY_TAG',
                targetTags: {
                  some: {
                    tagId: { in: userTagIds.length > 0 ? userTagIds : ['__none__'] },
                  },
                },
              },
              // SPECIFIC: 指定给该用户
              {
                targetType: 'SPECIFIC',
                targetUsers: {
                  some: { accountSequence: params.userSerialNum },
                },
              },
            ],
          },
        ],
      },
      include: {
        readRecords: {
          where: { userSerialNum: params.userSerialNum },
          take: 1,
        },
        targetTags: { select: { tagId: true } },
        targetUsers: { select: { accountSequence: true } },
      },
      orderBy: [{ priority: 'desc' }, { publishedAt: 'desc' }],
      take: params.limit ?? 50,
      skip: params.offset ?? 0,
    });

    return notifications.map((n) => ({
      notification: this.mapper.toDomain(n as NotificationWithTargets),
      isRead: n.readRecords.length > 0,
      readAt: n.readRecords[0]?.readAt ?? null,
    }));
  }

  async countUnreadForUser(userSerialNum: string): Promise<number> {
    const now = new Date();

    // 获取用户的标签
    const userTagIds = await this.tagRepository.findUserTagIds(userSerialNum);

    const count = await this.prisma.notification.count({
      where: {
        isEnabled: true,
        readRecords: {
          none: { userSerialNum },
        },
        AND: [
          // publishedAt 为 null 表示立即发布，或者 publishedAt <= now
          { OR: [{ publishedAt: null }, { publishedAt: { lte: now } }] },
          // expiresAt 为 null 表示永不过期，或者 expiresAt > now
          { OR: [{ expiresAt: null }, { expiresAt: { gt: now } }] },
          // 目标用户筛选
          {
            OR: [
              { targetType: 'ALL' },
              {
                targetType: 'BY_TAG',
                targetTags: {
                  some: {
                    tagId: { in: userTagIds.length > 0 ? userTagIds : ['__none__'] },
                  },
                },
              },
              {
                targetType: 'SPECIFIC',
                targetUsers: {
                  some: { accountSequence: userSerialNum },
                },
              },
            ],
          },
        ],
      },
    });
    return count;
  }

  async markAsRead(
    notificationId: string,
    userSerialNum: string,
  ): Promise<void> {
    await this.prisma.notificationRead.upsert({
      where: {
        notificationId_userSerialNum: {
          notificationId,
          userSerialNum,
        },
      },
      create: {
        notificationId,
        userSerialNum,
      },
      update: {},
    });
  }

  async markAllAsRead(userSerialNum: string): Promise<void> {
    const now = new Date();

    // 获取用户的标签
    const userTagIds = await this.tagRepository.findUserTagIds(userSerialNum);

    // 获取所有未读的有效通知
    const unreadNotifications = await this.prisma.notification.findMany({
      where: {
        isEnabled: true,
        readRecords: {
          none: { userSerialNum },
        },
        AND: [
          // publishedAt 为 null 表示立即发布，或者 publishedAt <= now
          { OR: [{ publishedAt: null }, { publishedAt: { lte: now } }] },
          // expiresAt 为 null 表示永不过期，或者 expiresAt > now
          { OR: [{ expiresAt: null }, { expiresAt: { gt: now } }] },
          // 目标用户筛选
          {
            OR: [
              { targetType: 'ALL' },
              {
                targetType: 'BY_TAG',
                targetTags: {
                  some: {
                    tagId: { in: userTagIds.length > 0 ? userTagIds : ['__none__'] },
                  },
                },
              },
              {
                targetType: 'SPECIFIC',
                targetUsers: {
                  some: { accountSequence: userSerialNum },
                },
              },
            ],
          },
        ],
      },
      select: { id: true },
    });

    // 批量创建已读记录
    if (unreadNotifications.length > 0) {
      await this.prisma.notificationRead.createMany({
        data: unreadNotifications.map((n) => ({
          notificationId: n.id,
          userSerialNum,
        })),
        skipDuplicates: true,
      });
    }
  }

  async delete(id: string): Promise<void> {
    await this.prisma.notification.delete({
      where: { id },
    });
  }

  async findAll(params?: {
    type?: NotificationType;
    isEnabled?: boolean;
    limit?: number;
    offset?: number;
  }): Promise<NotificationEntity[]> {
    const notifications = await this.prisma.notification.findMany({
      where: {
        ...(params?.type && { type: params.type }),
        ...(params?.isEnabled !== undefined && { isEnabled: params.isEnabled }),
      },
      include: {
        targetTags: { select: { tagId: true } },
        targetUsers: { select: { accountSequence: true } },
      },
      orderBy: { createdAt: 'desc' },
      take: params?.limit ?? 50,
      skip: params?.offset ?? 0,
    });
    return notifications.map((n) =>
      this.mapper.toDomain(n as NotificationWithTargets),
    );
  }

  async count(params?: {
    type?: NotificationType;
    isEnabled?: boolean;
  }): Promise<number> {
    return this.prisma.notification.count({
      where: {
        ...(params?.type && { type: params.type }),
        ...(params?.isEnabled !== undefined && { isEnabled: params.isEnabled }),
      },
    });
  }
}
