import { Injectable, ForbiddenException, NotFoundException } from '@nestjs/common';
import { PrismaService } from '@chatapp/database';

@Injectable()
export class InformationBarriersService {
  constructor(private readonly prisma: PrismaService) {}

  private async requireAdmin(wsId: string, userId: string) {
    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId } },
    });
    if (!member || (member.role !== 'admin' && member.role !== 'owner')) {
      throw new ForbiddenException('Admin access required');
    }
    return member;
  }

  async list(wsId: string, userId: string) {
    await this.requireAdmin(wsId, userId);

    const barriers = await this.prisma.informationBarrier.findMany({
      where: { workspaceId: wsId },
      orderBy: { createdAt: 'desc' },
      include: {
        createdByUser: { select: { id: true, displayName: true, avatarUrl: true } },
      },
    });

    return {
      barriers: barriers.map(b => ({
        id: b.id,
        name: b.name,
        group_a: { type: b.groupAType, ids: b.groupAIds },
        group_b: { type: b.groupBType, ids: b.groupBIds },
        is_active: b.isActive,
        created_by: b.createdByUser,
        created_at: b.createdAt,
      })),
    };
  }

  async create(wsId: string, userId: string, dto: any) {
    await this.requireAdmin(wsId, userId);

    const barrier = await this.prisma.informationBarrier.create({
      data: {
        workspaceId: wsId,
        name: dto.name,
        groupAType: dto.group_a_type || 'user_group',
        groupAIds: dto.group_a_ids || [],
        groupBType: dto.group_b_type || 'user_group',
        groupBIds: dto.group_b_ids || [],
        createdBy: userId,
        isActive: true,
      },
      include: {
        createdByUser: { select: { id: true, displayName: true, avatarUrl: true } },
      },
    });

    return {
      id: barrier.id,
      name: barrier.name,
      group_a: { type: barrier.groupAType, ids: barrier.groupAIds },
      group_b: { type: barrier.groupBType, ids: barrier.groupBIds },
      is_active: barrier.isActive,
      created_by: barrier.createdByUser,
      created_at: barrier.createdAt,
    };
  }

  async update(id: string, wsId: string, userId: string, dto: any) {
    await this.requireAdmin(wsId, userId);

    const barrier = await this.prisma.informationBarrier.findFirst({
      where: { id, workspaceId: wsId },
    });
    if (!barrier) throw new NotFoundException('Information barrier not found');

    const data: any = {};
    if (dto.name !== undefined) data.name = dto.name;
    if (dto.group_a_type !== undefined) data.groupAType = dto.group_a_type;
    if (dto.group_a_ids !== undefined) data.groupAIds = dto.group_a_ids;
    if (dto.group_b_type !== undefined) data.groupBType = dto.group_b_type;
    if (dto.group_b_ids !== undefined) data.groupBIds = dto.group_b_ids;
    if (dto.is_active !== undefined) data.isActive = dto.is_active;

    return this.prisma.informationBarrier.update({ where: { id }, data });
  }

  async delete(id: string, wsId: string, userId: string) {
    await this.requireAdmin(wsId, userId);

    const barrier = await this.prisma.informationBarrier.findFirst({
      where: { id, workspaceId: wsId },
    });
    if (!barrier) throw new NotFoundException('Information barrier not found');

    await this.prisma.informationBarrier.delete({ where: { id } });
    return { deleted: true };
  }

  /**
   * Verifica si dos usuarios están separados por una barrera de información.
   */
  async isBarrierBetweenUsers(wsId: string, userA: string, userB: string): Promise<boolean> {
    const barriers = await this.prisma.informationBarrier.findMany({
      where: { workspaceId: wsId, isActive: true },
    });

    for (const b of barriers) {
      const aInGroupA = b.groupAIds.includes(userA);
      const aInGroupB = b.groupBIds.includes(userA);
      const bInGroupA = b.groupAIds.includes(userB);
      const bInGroupB = b.groupBIds.includes(userB);

      // If they are in opposite groups, barrier applies
      if ((aInGroupA && bInGroupB) || (aInGroupB && bInGroupA)) {
        return true;
      }
    }

    return false;
  }
}