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

@Injectable()
export class ConnectService {
  private readonly logger = new Logger(ConnectService.name);

  constructor(private readonly prisma: PrismaService) {}

  /**
   * Create a shared channel invitation from source workspace to target workspace.
   */
  async createSharedChannelInvite(data: {
    channelId: string;
    sourceWorkspaceId: string;
    targetWorkspaceId: string;
    targetChannelName?: string;
    targetChannelTopic?: string;
    targetIsPrivate?: boolean;
    createdBy: string;
  }) {
    // Verify the channel exists and belongs to the source workspace
    const channel = await this.prisma.channel.findUnique({
      where: { id: data.channelId },
    });

    if (!channel) throw new NotFoundException('Channel not found');
    if (channel.workspaceId !== data.sourceWorkspaceId) {
      throw new ForbiddenException('Channel does not belong to this workspace');
    }

    // Verify the creator is an admin/owner of the source workspace
    const membership = await this.prisma.workspaceMember.findFirst({
      where: { workspaceId: data.sourceWorkspaceId, userId: data.createdBy },
    });

    if (!membership || !['owner', 'admin'].includes(membership.role)) {
      throw new ForbiddenException('Only workspace admins can share channels');
    }

    // Check if an invitation already exists
    const existing = await this.prisma.sharedChannel.findFirst({
      where: {
        channelId: data.channelId,
        targetWorkspaceId: data.targetWorkspaceId,
        OR: [
          { status: 'pending' },
          { status: 'active' },
        ],
      },
    });

    if (existing) {
      throw new BadRequestException('A shared channel invitation already exists');
    }

    const invite = await this.prisma.sharedChannel.create({
      data: {
        channelId: data.channelId,
        sourceWorkspaceId: data.sourceWorkspaceId,
        targetWorkspaceId: data.targetWorkspaceId,
        targetChannelName: data.targetChannelName || channel.name,
        targetChannelTopic: data.targetChannelTopic || channel.topic || null,
        targetIsPrivate: data.targetIsPrivate || false,
        status: 'pending',
        createdBy: data.createdBy,
      },
    });

    this.logger.log(`Shared channel invite created: ${invite.id}`);
    return invite;
  }

  /**
   * List pending shared channel invitations for a workspace (as target).
   */
  async listIncomingInvites(workspaceId: string) {
    return this.prisma.sharedChannel.findMany({
      where: { targetWorkspaceId: workspaceId, status: 'pending' },
      include: {
        channel: {
          select: { id: true, name: true, topic: true },
        },
      },
      orderBy: { createdAt: 'desc' },
    });
  }

  /**
   * List outgoing shared channel invitations (as source).
   */
  async listOutgoingInvites(workspaceId: string) {
    return this.prisma.sharedChannel.findMany({
      where: { sourceWorkspaceId: workspaceId },
      include: {
        channel: {
          select: { id: true, name: true },
        },
      },
      orderBy: { createdAt: 'desc' },
    });
  }

  /**
   * Accept a shared channel invitation.
   * Creates a mirror channel in the target workspace and marks the invite as accepted.
   */
  async acceptInvite(inviteId: string, userId: string) {
    const invite = await this.prisma.sharedChannel.findUnique({
      where: { id: inviteId },
      include: { channel: true },
    });

    if (!invite) throw new NotFoundException('Invitation not found');
    if (invite.status !== 'pending') throw new BadRequestException('Invitation is not pending');

    // Verify the user is an admin of the target workspace
    const membership = await this.prisma.workspaceMember.findFirst({
      where: { workspaceId: invite.targetWorkspaceId, userId },
    });

    if (!membership || !['owner', 'admin'].includes(membership.role)) {
      throw new ForbiddenException('Only workspace admins can accept shared channels');
    }

    // Create a mirror channel in the target workspace
    const mirrorChannel = await this.prisma.channel.create({
      data: {
        workspaceId: invite.targetWorkspaceId,
        name: invite.targetChannelName || invite.channel.name,
        handle: `shared-${invite.channel.name.toLowerCase().replace(/\s+/g, '-')}`,
        type: invite.targetIsPrivate ? 'private' : 'public',
        topic: invite.targetChannelTopic || invite.channel.topic || null,
        createdBy: userId,
        isShared: true,
      },
    });

    // Add the accepting admin as a member of the mirror channel
    await this.prisma.channelMember.create({
      data: {
        channelId: mirrorChannel.id,
        userId,
        workspaceId: invite.targetWorkspaceId,
        role: 'admin',
        joinedAt: new Date(),
      },
    });

    // Mark invite as accepted
    const updated = await this.prisma.sharedChannel.update({
      where: { id: inviteId },
      data: {
        status: 'active',
        acceptedAt: new Date(),
        acceptedBy: userId,
      },
    });

    this.logger.log(`Shared channel accepted: ${inviteId} -> mirror channel ${mirrorChannel.id}`);
    return {
      invite: updated,
      mirrorChannel,
    };
  }

  /**
   * Reject a shared channel invitation.
   */
  async rejectInvite(inviteId: string, userId: string) {
    const invite = await this.prisma.sharedChannel.findUnique({
      where: { id: inviteId },
    });

    if (!invite) throw new NotFoundException('Invitation not found');
    if (invite.status !== 'pending') throw new BadRequestException('Invitation is not pending');

    const membership = await this.prisma.workspaceMember.findFirst({
      where: { workspaceId: invite.targetWorkspaceId, userId },
    });

    if (!membership || !['owner', 'admin'].includes(membership.role)) {
      throw new ForbiddenException('Only workspace admins can reject shared channels');
    }

    return this.prisma.sharedChannel.update({
      where: { id: inviteId },
      data: { status: 'rejected' },
    });
  }

  /**
   * Disconnect a previously accepted shared channel.
   */
  async disconnect(inviteId: string, userId: string) {
    const invite = await this.prisma.sharedChannel.findUnique({
      where: { id: inviteId },
    });

    if (!invite) throw new NotFoundException('Shared channel not found');
    if (invite.status !== 'active') throw new BadRequestException('Shared channel is not active');

    // Verify the user is an admin of either workspace
    const sourceMembership = await this.prisma.workspaceMember.findFirst({
      where: { workspaceId: invite.sourceWorkspaceId, userId },
    });
    const targetMembership = await this.prisma.workspaceMember.findFirst({
      where: { workspaceId: invite.targetWorkspaceId, userId },
    });

    const isSourceAdmin = sourceMembership && ['owner', 'admin'].includes(sourceMembership.role);
    const isTargetAdmin = targetMembership && ['owner', 'admin'].includes(targetMembership.role);

    if (!isSourceAdmin && !isTargetAdmin) {
      throw new ForbiddenException('Only workspace admins can disconnect shared channels');
    }

    await this.prisma.sharedChannel.update({
      where: { id: inviteId },
      data: { status: 'disconnected' },
    });

    return { disconnected: true };
  }

  /**
   * List all active shared channels for a workspace.
   */
  async listActiveShared(workspaceId: string) {
    const [outgoing, incoming] = await Promise.all([
      this.prisma.sharedChannel.findMany({
        where: { sourceWorkspaceId: workspaceId, status: 'active' },
        include: { channel: { select: { id: true, name: true } } },
      }),
      this.prisma.sharedChannel.findMany({
        where: { targetWorkspaceId: workspaceId, status: 'active' },
        include: { channel: { select: { id: true, name: true } } },
      }),
    ]);

    return { outgoing, incoming };
  }
}