/**
 * ProjectMemberService - Project Team Member Management Service
 *
 * Provides team member management for projects.
 * Supports multi-tenant architecture with ServiceContext pattern.
 *
 * @module Projects
 */

import { DataSource, Repository } from 'typeorm';
import { ProjectMemberEntity, ProjectMemberRole } from '../entities/project-member.entity';
import { ServiceContext, PaginatedResult } from './project.service';

export interface AddMemberDto {
  projectId: string;
  userId: string;
  role?: string;
}

export interface UpdateMemberRoleDto {
  role: string;
}

export interface ProjectMemberFilters {
  projectId?: string;
  userId?: string;
  role?: string;
}

export class ProjectMemberService {
  private repository: Repository<ProjectMemberEntity>;

  constructor(dataSource: DataSource) {
    this.repository = dataSource.getRepository(ProjectMemberEntity);
  }

  /**
   * Find member by ID
   */
  async findById(ctx: ServiceContext, id: string): Promise<ProjectMemberEntity | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      },
    });
  }

  /**
   * Find member by project and user
   */
  async findByProjectAndUser(
    ctx: ServiceContext,
    projectId: string,
    userId: string
  ): Promise<ProjectMemberEntity | null> {
    return this.repository.findOne({
      where: {
        projectId,
        userId,
        tenantId: ctx.tenantId,
      },
    });
  }

  /**
   * Find all members with pagination and filters
   */
  async findAll(
    ctx: ServiceContext,
    filters: ProjectMemberFilters = {},
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<ProjectMemberEntity>> {
    const skip = (page - 1) * limit;

    const qb = this.repository
      .createQueryBuilder('pm')
      .where('pm.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.projectId) {
      qb.andWhere('pm.project_id = :projectId', { projectId: filters.projectId });
    }
    if (filters.userId) {
      qb.andWhere('pm.user_id = :userId', { userId: filters.userId });
    }
    if (filters.role) {
      qb.andWhere('pm.role = :role', { role: filters.role });
    }

    qb.orderBy('pm.created_at', 'DESC').skip(skip).take(limit);

    const [data, total] = await qb.getManyAndCount();

    return {
      data,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  /**
   * Add a member to a project
   */
  async addMember(ctx: ServiceContext, dto: AddMemberDto): Promise<ProjectMemberEntity> {
    // Check if already a member
    const existing = await this.findByProjectAndUser(ctx, dto.projectId, dto.userId);
    if (existing) {
      throw new Error('User is already a member of this project');
    }

    const entity = this.repository.create({
      tenantId: ctx.tenantId,
      projectId: dto.projectId,
      userId: dto.userId,
      role: dto.role || ProjectMemberRole.MEMBER,
    });

    return this.repository.save(entity);
  }

  /**
   * Update member role
   */
  async updateRole(
    ctx: ServiceContext,
    id: string,
    dto: UpdateMemberRoleDto
  ): Promise<ProjectMemberEntity | null> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return null;
    }

    entity.role = dto.role;

    return this.repository.save(entity);
  }

  /**
   * Remove a member from a project
   */
  async removeMember(ctx: ServiceContext, id: string): Promise<boolean> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return false;
    }

    await this.repository.delete(id);
    return true;
  }

  /**
   * Remove a member by project and user
   */
  async removeMemberByProjectAndUser(
    ctx: ServiceContext,
    projectId: string,
    userId: string
  ): Promise<boolean> {
    const entity = await this.findByProjectAndUser(ctx, projectId, userId);
    if (!entity) {
      return false;
    }

    await this.repository.delete(entity.id);
    return true;
  }

  /**
   * Find members by project
   */
  async findByProject(
    ctx: ServiceContext,
    projectId: string,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<ProjectMemberEntity>> {
    return this.findAll(ctx, { projectId }, page, limit);
  }

  /**
   * Find projects where user is a member
   */
  async findProjectsByUser(
    ctx: ServiceContext,
    userId: string,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<ProjectMemberEntity>> {
    return this.findAll(ctx, { userId }, page, limit);
  }

  /**
   * Check if user is a member of a project
   */
  async isMember(ctx: ServiceContext, projectId: string, userId: string): Promise<boolean> {
    const member = await this.findByProjectAndUser(ctx, projectId, userId);
    return member !== null;
  }

  /**
   * Get user role in a project
   */
  async getUserRole(
    ctx: ServiceContext,
    projectId: string,
    userId: string
  ): Promise<string | null> {
    const member = await this.findByProjectAndUser(ctx, projectId, userId);
    return member?.role || null;
  }

  /**
   * Count members in a project
   */
  async countMembers(ctx: ServiceContext, projectId: string): Promise<number> {
    return this.repository.count({
      where: { projectId, tenantId: ctx.tenantId },
    });
  }

  /**
   * Add multiple members to a project
   */
  async addMembers(
    ctx: ServiceContext,
    projectId: string,
    userIds: string[],
    role?: string
  ): Promise<ProjectMemberEntity[]> {
    const added: ProjectMemberEntity[] = [];

    for (const userId of userIds) {
      try {
        const member = await this.addMember(ctx, {
          projectId,
          userId,
          role: role || ProjectMemberRole.MEMBER,
        });
        added.push(member);
      } catch {
        // Skip if already a member
      }
    }

    return added;
  }

  /**
   * Remove all members from a project
   */
  async removeAllMembers(ctx: ServiceContext, projectId: string): Promise<number> {
    const result = await this.repository.delete({
      projectId,
      tenantId: ctx.tenantId,
    });

    return result.affected || 0;
  }
}
