import { prisma } from '../../config/database';
import type { SocialGroupType } from '@prisma/client';
import { generateSlug } from '../jitsi/jitsi.utils';
import { notificationService } from './notification.service';

const MEMBER_USER_SELECT = {
  id: true,
  name: true,
  email: true,
  role: true,
} as const;

const MEETING_SELECT = {
  id: true,
  slug: true,
  isActive: true,
} as const;

export const groupService = {
  /** Get or create a shift team group */
  async getOrCreateShiftTeam(shiftId: string) {
    const shift = await prisma.shift.findUnique({
      where: { id: shiftId },
      select: { id: true, title: true },
    });
    if (!shift) throw Object.assign(new Error('Shift not found'), { statusCode: 404 });

    return this.getOrCreateGroup('SHIFT_TEAM', String(shift.id), `${shift.title} Team`);
  },

  /** Get or create a campaign team group */
  async getOrCreateCampaignTeam(campaignId: string) {
    const campaign = await prisma.campaign.findUnique({
      where: { id: campaignId },
      select: { id: true, title: true },
    });
    if (!campaign) throw Object.assign(new Error('Campaign not found'), { statusCode: 404 });

    return this.getOrCreateGroup('CAMPAIGN_TEAM', campaign.id, `${campaign.title} Team`);
  },

  /** Generic get-or-create for a group */
  async getOrCreateGroup(type: SocialGroupType, referenceId: string, name: string) {
    let group = await prisma.socialGroup.findUnique({
      where: { type_referenceId: { type, referenceId } },
      include: {
        members: {
          include: { user: { select: MEMBER_USER_SELECT } },
          orderBy: { joinedAt: 'asc' },
        },
      },
    });

    if (!group) {
      group = await prisma.socialGroup.create({
        data: { type, referenceId, name },
        include: {
          members: {
            include: { user: { select: MEMBER_USER_SELECT } },
            orderBy: { joinedAt: 'asc' },
          },
        },
      });
    }

    return group;
  },

  /** Sync shift team membership from actual signups */
  async syncShiftTeam(shiftId: string) {
    // Get confirmed signups with user IDs
    const signups = await prisma.shiftSignup.findMany({
      where: { shiftId, status: 'CONFIRMED', userId: { not: null } },
      select: { userId: true },
    });

    const userIds = signups.map((s) => s.userId).filter(Boolean) as string[];
    if (userIds.length < 2) return; // Only create team if 2+ members

    const group = await this.getOrCreateShiftTeam(shiftId);

    // Get existing member IDs
    const existingIds = new Set(group.members.map((m) => m.userId));

    // Add new members
    const newUserIds = userIds.filter((id) => !existingIds.has(id));
    if (newUserIds.length > 0) {
      await prisma.socialGroupMember.createMany({
        data: newUserIds.map((userId) => ({
          groupId: group.id,
          userId,
        })),
        skipDuplicates: true,
      });
    }

    // Remove members who cancelled
    const currentIds = new Set(userIds);
    const removedIds = group.members
      .map((m) => m.userId)
      .filter((id) => !currentIds.has(id));

    if (removedIds.length > 0) {
      await prisma.socialGroupMember.deleteMany({
        where: {
          groupId: group.id,
          userId: { in: removedIds },
        },
      });
    }
  },

  /** Sync campaign team membership from email participants */
  async syncCampaignTeam(campaignId: string) {
    const emails = await prisma.campaignEmail.findMany({
      where: { campaignId, userId: { not: null } },
      distinct: ['userId'],
      select: { userId: true },
    });

    const userIds = emails.map((e) => e.userId).filter(Boolean) as string[];
    if (userIds.length < 2) return;

    const group = await this.getOrCreateCampaignTeam(campaignId);
    const existingIds = new Set(group.members.map((m) => m.userId));
    const newUserIds = userIds.filter((id) => !existingIds.has(id));

    if (newUserIds.length > 0) {
      await prisma.socialGroupMember.createMany({
        data: newUserIds.map((userId) => ({
          groupId: group.id,
          userId,
        })),
        skipDuplicates: true,
      });
    }
  },

  /** List all groups a user belongs to */
  async listMyGroups(userId: string, page: number, limit: number) {
    const skip = (page - 1) * limit;

    const [memberships, total] = await Promise.all([
      prisma.socialGroupMember.findMany({
        where: { userId },
        include: {
          group: {
            include: {
              _count: { select: { members: true } },
              meeting: { select: MEETING_SELECT },
            },
          },
        },
        orderBy: { joinedAt: 'desc' },
        skip,
        take: limit,
      }),
      prisma.socialGroupMember.count({ where: { userId } }),
    ]);

    return {
      groups: memberships.map((m) => ({
        id: m.group.id,
        name: m.group.name,
        type: m.group.type,
        referenceId: m.group.referenceId,
        memberCount: m.group._count.members,
        joinedAt: m.joinedAt,
        hasActiveCall: m.group.meeting?.isActive === true,
      })),
      pagination: { page, limit, total, totalPages: Math.ceil(total / limit) },
    };
  },

  /** Get group details with members */
  async getGroupDetail(groupId: string) {
    const group = await prisma.socialGroup.findUnique({
      where: { id: groupId },
      include: {
        members: {
          include: { user: { select: MEMBER_USER_SELECT } },
          orderBy: { joinedAt: 'asc' },
        },
        meeting: { select: MEETING_SELECT },
      },
    });

    if (!group) throw Object.assign(new Error('Group not found'), { statusCode: 404 });

    return {
      id: group.id,
      name: group.name,
      type: group.type,
      referenceId: group.referenceId,
      createdAt: group.createdAt,
      members: group.members.map((m) => ({
        id: m.id,
        userId: m.userId,
        joinedAt: m.joinedAt,
        user: m.user,
      })),
      memberCount: group.members.length,
      meeting: group.meeting,
    };
  },

  /** Start a group call — create or reactivate meeting */
  async startGroupCall(groupId: string, userId: string) {
    const group = await prisma.socialGroup.findUnique({
      where: { id: groupId },
      include: {
        members: { select: { userId: true } },
        meeting: true,
      },
    });
    if (!group) throw Object.assign(new Error('Group not found'), { statusCode: 404 });

    // Verify membership
    const isMember = group.members.some((m) => m.userId === userId);
    if (!isMember) throw Object.assign(new Error('Not a member of this group'), { statusCode: 403 });

    let meeting = group.meeting;

    if (meeting) {
      // Reactivate existing meeting
      meeting = await prisma.meeting.update({
        where: { id: meeting.id },
        data: { isActive: true },
      });
    } else {
      // Create new meeting and link to group
      const slug = generateSlug(`${group.name} call`);
      meeting = await prisma.meeting.create({
        data: {
          title: `${group.name} — Group Call`,
          slug,
          jitsiRoom: slug,
          isActive: true,
          createdByUserId: userId,
        },
      });
      await prisma.socialGroup.update({
        where: { id: groupId },
        data: { meetingId: meeting.id },
      });
    }

    // Notify all other group members
    const otherMembers = group.members.filter((m) => m.userId !== userId);
    const caller = await prisma.user.findUnique({ where: { id: userId }, select: { name: true, email: true } });
    const callerName = caller?.name || caller?.email || 'A member';

    await Promise.allSettled(
      otherMembers.map((m) =>
        notificationService.createNotification(
          m.userId,
          'group_call',
          `${group.name} — Group Call`,
          `${callerName} started a call in ${group.name}`,
          { groupId, meetingSlug: meeting!.slug },
        )
      )
    );

    return { id: meeting.id, slug: meeting.slug, isActive: meeting.isActive };
  },

  /** End a group call — deactivate meeting */
  async endGroupCall(groupId: string, userId: string) {
    const group = await prisma.socialGroup.findUnique({
      where: { id: groupId },
      include: {
        members: { select: { userId: true } },
        meeting: true,
      },
    });
    if (!group) throw Object.assign(new Error('Group not found'), { statusCode: 404 });

    const isMember = group.members.some((m) => m.userId === userId);
    if (!isMember) throw Object.assign(new Error('Not a member of this group'), { statusCode: 403 });

    if (!group.meeting) throw Object.assign(new Error('No active call'), { statusCode: 404 });

    await prisma.meeting.update({
      where: { id: group.meeting.id },
      data: { isActive: false },
    });

    return { success: true };
  },

  /** Verify membership and active call, return meeting slug for token generation */
  async getCallMeeting(groupId: string, userId: string) {
    const group = await prisma.socialGroup.findUnique({
      where: { id: groupId },
      include: {
        members: { select: { userId: true } },
        meeting: true,
      },
    });
    if (!group) throw Object.assign(new Error('Group not found'), { statusCode: 404 });

    const isMember = group.members.some((m) => m.userId === userId);
    if (!isMember) throw Object.assign(new Error('Not a member of this group'), { statusCode: 403 });

    if (!group.meeting?.isActive) throw Object.assign(new Error('No active call'), { statusCode: 404 });

    return group.meeting;
  },
};
