// common/src/repositories/organization-member.repository.ts
import { Prisma, organization_members } from "@prisma/client"
import { prisma } from "../prisma-client"
import { BaseRepository } from "./base-repository"

export class OrganizationMemberRepository extends BaseRepository<
  organization_members,
  Prisma.organization_membersCreateInput,
  Prisma.organization_membersUpdateInput
> {
  protected get model() {
    return this.prisma.organization_members
  }

  /**
   * Find member by organization and user
   */
  async findByOrgAndUser(
    organizationId: string,
    userId: string,
  ): Promise<organization_members | null> {
    return await this.model.findUnique({
      where: {
        organization_id_user_id: {
          organization_id: organizationId,
          user_id: userId,
        },
      },
    })
  }

  /**
   * Find all members of an organization
   */
  async findByOrganization(
    organizationId: string,
    options?: {
      includeUser?: boolean
      role?: string
    },
  ): Promise<any[]> {
    return await this.model.findMany({
      where: {
        organization_id: organizationId,
        ...(options?.role && { role: options.role }),
      },
      include: {
        user: options?.includeUser
          ? {
              select: {
                user_id: true,
                name: true,
                email: true,
                github_username: true,
              },
            }
          : false,
      },
      orderBy: { added_at: "desc" },
    })
  }

  /**
   * Find all organizations for a user
   */
  async findByUser(
    userId: string,
    options?: {
      includeOrganization?: boolean
      role?: string
    },
  ): Promise<any[]> {
    return await this.model.findMany({
      where: {
        user_id: userId,
        ...(options?.role && { role: options.role }),
      },
      include: {
        organization: options?.includeOrganization
          ? {
              select: {
                id: true,
                name: true,
                github_org_id: true,
                description: true,
                website: true,
              },
            }
          : false,
      },
      orderBy: { added_at: "desc" },
    })
  }

  /**
   * Add a member to an organization
   */
  async addMember(data: {
    organizationId: string
    userId: string
    role: string
    addedBy?: string
  }): Promise<organization_members> {
    return await this.model.create({
      data: {
        organization: {
          connect: { id: data.organizationId },
        },
        user: {
          connect: { user_id: data.userId },
        },
        role: data.role,
        added_by: data.addedBy,
      },
    })
  }

  /**
   * Update member role
   */
  async updateRole(
    organizationId: string,
    userId: string,
    role: string,
  ): Promise<organization_members> {
    return await this.model.update({
      where: {
        organization_id_user_id: {
          organization_id: organizationId,
          user_id: userId,
        },
      },
      data: { role },
    })
  }

  /**
   * Remove a member from an organization
   */
  async removeMember(organizationId: string, userId: string): Promise<organization_members> {
    return await this.model.delete({
      where: {
        organization_id_user_id: {
          organization_id: organizationId,
          user_id: userId,
        },
      },
    })
  }

  /**
   * Check if user is a member of organization
   */
  async isMember(organizationId: string, userId: string): Promise<boolean> {
    const member = await this.findByOrgAndUser(organizationId, userId)
    return member !== null
  }

  /**
   * Check if user has specific role in organization
   */
  async hasRole(organizationId: string, userId: string, role: string): Promise<boolean> {
    const member = await this.findByOrgAndUser(organizationId, userId)
    return member?.role === role
  }

  /**
   * Get member count for an organization
   */
  async getMemberCount(organizationId: string): Promise<number> {
    return await this.model.count({
      where: { organization_id: organizationId },
    })
  }

  /**
   * Get member count by role for an organization
   */
  async getMemberCountByRole(organizationId: string): Promise<Record<string, number>> {
    const members = await this.model.groupBy({
      by: ["role"],
      where: { organization_id: organizationId },
      _count: { role: true },
    })

    return members.reduce(
      (acc, curr) => {
        acc[curr.role] = curr._count.role
        return acc
      },
      {} as Record<string, number>,
    )
  }

  /**
   * Find members with full details (user and organization)
   */
  async findByIdWithRelations(id: string): Promise<any | null> {
    return await this.model.findUnique({
      where: { id },
      include: {
        user: {
          select: {
            user_id: true,
            name: true,
            email: true,
            github_username: true,
          },
        },
        organization: {
          select: {
            id: true,
            name: true,
            github_org_id: true,
            description: true,
          },
        },
      },
    })
  }

  /**
   * Bulk add members to an organization
   */
  async addMembers(data: {
    organizationId: string
    members: Array<{
      userId: string
      role: string
    }>
    addedBy?: string
  }): Promise<Prisma.BatchPayload> {
    return await this.model.createMany({
      data: data.members.map(member => ({
        organization_id: data.organizationId,
        user_id: member.userId,
        role: member.role,
        added_by: data.addedBy,
      })),
      skipDuplicates: true,
    })
  }

  /**
   * Bulk remove members from an organization
   */
  async removeMembers(organizationId: string, userIds: string[]): Promise<Prisma.BatchPayload> {
    return await this.model.deleteMany({
      where: {
        organization_id: organizationId,
        user_id: { in: userIds },
      },
    })
  }

  /**
   * Find members added by a specific user
   */
  async findAddedBy(addedBy: string): Promise<organization_members[]> {
    return await this.model.findMany({
      where: { added_by: addedBy },
      include: {
        user: {
          select: {
            user_id: true,
            name: true,
            email: true,
          },
        },
        organization: {
          select: {
            id: true,
            name: true,
          },
        },
      },
      orderBy: { added_at: "desc" },
    })
  }

  /**
   * Get organization admins
   */
  async getAdmins(organizationId: string): Promise<any[]> {
    return await this.findByOrganization(organizationId, {
      includeUser: true,
      role: "admin",
    })
  }

  /**
   * change organization ownership or change admin
   */
  async changeRole(
    organizationId: string,
    fromUserId: string,
    toUserId: string,
    newRole: string,
  ): Promise<void> {
    await this.prisma.$transaction([
      // Update the current user's role
      this.model.update({
        where: {
          organization_id_user_id: {
            organization_id: organizationId,
            user_id: fromUserId,
          },
        },
        data: { role: "member" },
      }),
      // Update the new user's role
      this.model.update({
        where: {
          organization_id_user_id: {
            organization_id: organizationId,
            user_id: toUserId,
          },
        },
        data: { role: newRole },
      }),
    ])
  }

  /**
   * Search members by name or email
   */
  async searchMembers(
    organizationId: string,
    searchTerm: string,
    limit: number = 10,
  ): Promise<any[]> {
    return await this.model.findMany({
      where: {
        organization_id: organizationId,
        user: {
          OR: [
            { name: { contains: searchTerm, mode: "insensitive" } },
            { email: { contains: searchTerm, mode: "insensitive" } },
            { github_username: { contains: searchTerm, mode: "insensitive" } },
          ],
        },
      },
      include: {
        user: {
          select: {
            user_id: true,
            name: true,
            email: true,
            github_username: true,
          },
        },
      },
      take: limit,
      orderBy: { added_at: "desc" },
    })
  }

  /**
   * Get recent members
   */
  async getRecentMembers(organizationId: string, limit: number = 5): Promise<any[]> {
    return await this.model.findMany({
      where: { organization_id: organizationId },
      include: {
        user: {
          select: {
            user_id: true,
            name: true,
            email: true,
            github_username: true,
          },
        },
      },
      orderBy: { added_at: "desc" },
      take: limit,
    })
  }
}

export const organizationMemberRepository = new OrganizationMemberRepository(prisma)
