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

@Injectable()
export class GuestsService {
  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;
  }

  /**
   * Lista todos los miembros guest del workspace.
   * guestType puede ser 'multi_channel_guest' o 'single_channel_guest'.
   */
  async list(wsId: string, userId: string) {
    await this.requireAdmin(wsId, userId);

    const guests = await this.prisma.workspaceMember.findMany({
      where: {
        workspaceId: wsId,
        guestType: { not: null },
        deactivatedAt: null,
      },
      include: {
        user: {
          select: {
            id: true,
            email: true,
            displayName: true,
            realName: true,
            avatarUrl: true,
            title: true,
            lastActiveAt: true,
          },
        },
      },
      orderBy: { joinedAt: 'desc' },
    });

    return {
      guests: guests.map(g => ({
        id: g.id,
        user: g.user,
        guest_type: g.guestType,
        display_name: g.displayName,
        title: g.title,
        joined_at: g.joinedAt,
      })),
    };
  }

  /**
   * Invita un nuevo usuario como guest al workspace.
   * guest_type: 'multi_channel_guest' o 'single_channel_guest'.
   */
  async invite(wsId: string, userId: string, dto: { email: string; guest_type: string; channel_ids?: string[] }) {
    await this.requireAdmin(wsId, userId);

    if (!['multi_channel_guest', 'single_channel_guest'].includes(dto.guest_type)) {
      throw new BadRequestException('Invalid guest type. Use multi_channel_guest or single_channel_guest');
    }

    // Check if user already exists
    const existingUser = await this.prisma.user.findUnique({
      where: { email: dto.email },
    });

    if (existingUser) {
      // Check if already a member
      const existingMember = await this.prisma.workspaceMember.findUnique({
        where: { workspaceId_userId: { workspaceId: wsId, userId: existingUser.id } },
      });
      if (existingMember) {
        // Update to guest
        return this.prisma.workspaceMember.update({
          where: { workspaceId_userId: { workspaceId: wsId, userId: existingUser.id } },
          data: { guestType: dto.guest_type, role: 'member' },
        });
      }
    }

    // Create invitation with guest type encoded in role
    const token = randomBytes(32).toString('hex');
    const invitation = await this.prisma.workspaceInvitation.create({
      data: {
        workspaceId: wsId,
        email: dto.email,
        role: `guest:${dto.guest_type}`,
        token,
        invitedBy: userId,
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
      },
    });

    // If single channel guest and channel_ids provided, store for later
    // (We'd link channels after the guest accepts the invitation)

    return {
      invitation_id: invitation.id,
      email: invitation.email,
      guest_type: dto.guest_type,
      token,
      expires_at: invitation.expiresAt,
    };
  }

  /**
   * Cambia el tipo de guest de un miembro.
   */
  async updateGuestType(
    wsId: string,
    userId: string,
    targetUserId: string,
    dto: { guest_type: string | null },
  ) {
    await this.requireAdmin(wsId, userId);

    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId: targetUserId } },
    });
    if (!member) throw new NotFoundException('Member not found');

    let guestType: string | null = null;
    if (dto.guest_type) {
      if (!['multi_channel_guest', 'single_channel_guest'].includes(dto.guest_type)) {
        throw new BadRequestException('Invalid guest type');
      }
      guestType = dto.guest_type;
    }

    return this.prisma.workspaceMember.update({
      where: { workspaceId_userId: { workspaceId: wsId, userId: targetUserId } },
      data: { guestType },
    });
  }

  /**
   * Convierte un guest a miembro regular.
   */
  async convertToMember(wsId: string, userId: string, targetUserId: string) {
    return this.updateGuestType(wsId, userId, targetUserId, { guest_type: null });
  }

  /**
   * Lista los canales a los que un single_channel_guest tiene acceso.
   */
  async listGuestChannels(wsId: string, userId: string, targetUserId: string) {
    await this.requireAdmin(wsId, userId);

    const member = await this.prisma.workspaceMember.findUnique({
      where: { workspaceId_userId: { workspaceId: wsId, userId: targetUserId } },
    });
    if (!member) throw new NotFoundException('Member not found');

    const channels = await this.prisma.channel.findMany({
      where: {
        workspaceId: wsId,
        members: { some: { userId: targetUserId } },
      },
      select: {
        id: true,
        name: true,
        type: true,
        isArchived: true,
      },
    });

    return {
      guest_type: member.guestType,
      channels,
    };
  }
}