import type { PrismaClient, Group } from '@prisma/client';

export interface GroupWithMembers extends Group {
  members: Array<{ id: string; user: { id: string; email: string; name: string | null } }>;
}

export interface IGroupRepository {
  findAll(): Promise<GroupWithMembers[]>;
  findById(id: string): Promise<GroupWithMembers | null>;
  findByName(name: string): Promise<GroupWithMembers | null>;
  create(data: { name: string; description?: string }): Promise<Group>;
  update(id: string, data: { description?: string }): Promise<Group>;
  delete(id: string): Promise<void>;
  setMembers(groupId: string, userIds: string[]): Promise<void>;
  findGroupsForUser(userId: string): Promise<Array<{ id: string; name: string }>>;
}

const MEMBERS_INCLUDE = {
  members: {
    select: {
      id: true,
      user: {
        select: { id: true, email: true, name: true },
      },
    },
  },
} as const;

export class GroupRepository implements IGroupRepository {
  constructor(private readonly prisma: PrismaClient) {}

  async findAll(): Promise<GroupWithMembers[]> {
    return this.prisma.group.findMany({
      orderBy: { name: 'asc' },
      include: MEMBERS_INCLUDE,
    });
  }

  async findById(id: string): Promise<GroupWithMembers | null> {
    return this.prisma.group.findUnique({
      where: { id },
      include: MEMBERS_INCLUDE,
    });
  }

  async findByName(name: string): Promise<GroupWithMembers | null> {
    return this.prisma.group.findUnique({
      where: { name },
      include: MEMBERS_INCLUDE,
    });
  }

  async create(data: { name: string; description?: string }): Promise<Group> {
    const createData: Record<string, unknown> = { name: data.name };
    if (data.description !== undefined) createData['description'] = data.description;
    return this.prisma.group.create({
      data: createData as Parameters<PrismaClient['group']['create']>[0]['data'],
    });
  }

  async update(id: string, data: { description?: string }): Promise<Group> {
    const updateData: Record<string, unknown> = {};
    if (data.description !== undefined) updateData['description'] = data.description;
    return this.prisma.group.update({ where: { id }, data: updateData });
  }

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

  async setMembers(groupId: string, userIds: string[]): Promise<void> {
    await this.prisma.$transaction(async (tx) => {
      await tx.groupMember.deleteMany({ where: { groupId } });
      if (userIds.length > 0) {
        await tx.groupMember.createMany({
          data: userIds.map((userId) => ({ groupId, userId })),
        });
      }
    });
  }

  async findGroupsForUser(userId: string): Promise<Array<{ id: string; name: string }>> {
    const memberships = await this.prisma.groupMember.findMany({
      where: { userId },
      select: {
        group: {
          select: { id: true, name: true },
        },
      },
    });
    return memberships.map((m) => m.group);
  }
}
