/**
 * MilestoneService - Project Milestone Management Service
 *
 * Provides CRUD operations for project milestones.
 * Supports multi-tenant architecture with ServiceContext pattern.
 *
 * @module Projects
 */

import { DataSource, Repository } from 'typeorm';
import { MilestoneEntity, MilestoneStatus } from '../entities/milestone.entity';
import { ServiceContext, PaginatedResult } from './project.service';

export interface CreateMilestoneDto {
  projectId: string;
  name: string;
  description?: string;
  dateDeadline?: string;
}

export interface UpdateMilestoneDto {
  name?: string;
  description?: string | null;
  dateDeadline?: string | null;
  status?: MilestoneStatus;
}

export interface MilestoneFilters {
  projectId?: string;
  status?: MilestoneStatus;
  search?: string;
}

export class MilestoneService {
  private repository: Repository<MilestoneEntity>;

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

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

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

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

    if (filters.projectId) {
      qb.andWhere('m.project_id = :projectId', { projectId: filters.projectId });
    }
    if (filters.status) {
      qb.andWhere('m.status = :status', { status: filters.status });
    }
    if (filters.search) {
      qb.andWhere('m.name ILIKE :search', { search: `%${filters.search}%` });
    }

    qb.orderBy('m.date_deadline', 'ASC').addOrderBy('m.created_at', 'DESC').skip(skip).take(limit);

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

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

  /**
   * Create a new milestone
   */
  async create(ctx: ServiceContext, dto: CreateMilestoneDto): Promise<MilestoneEntity> {
    const entity = this.repository.create({
      tenantId: ctx.tenantId,
      projectId: dto.projectId,
      name: dto.name,
      description: dto.description || null,
      dateDeadline: dto.dateDeadline ? new Date(dto.dateDeadline) : null,
      status: MilestoneStatus.PENDING,
      createdBy: ctx.userId || null,
    });

    return this.repository.save(entity);
  }

  /**
   * Update a milestone
   */
  async update(
    ctx: ServiceContext,
    id: string,
    dto: UpdateMilestoneDto
  ): Promise<MilestoneEntity | null> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return null;
    }

    if (dto.name !== undefined) entity.name = dto.name;
    if (dto.description !== undefined) entity.description = dto.description;
    if (dto.dateDeadline !== undefined) {
      entity.dateDeadline = dto.dateDeadline ? new Date(dto.dateDeadline) : null;
    }
    if (dto.status !== undefined) entity.status = dto.status;

    entity.updatedBy = ctx.userId || null;

    return this.repository.save(entity);
  }

  /**
   * Delete a milestone
   */
  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const entity = await this.findById(ctx, id);
    if (!entity) {
      return false;
    }

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

  /**
   * Complete a milestone
   */
  async complete(ctx: ServiceContext, id: string): Promise<MilestoneEntity | null> {
    return this.update(ctx, id, { status: MilestoneStatus.COMPLETED });
  }

  /**
   * Cancel a milestone
   */
  async cancel(ctx: ServiceContext, id: string): Promise<MilestoneEntity | null> {
    return this.update(ctx, id, { status: MilestoneStatus.CANCELLED });
  }

  /**
   * Reset milestone to pending
   */
  async reset(ctx: ServiceContext, id: string): Promise<MilestoneEntity | null> {
    return this.update(ctx, id, { status: MilestoneStatus.PENDING });
  }

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

  /**
   * Find pending milestones
   */
  async findPending(
    ctx: ServiceContext,
    projectId?: string,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<MilestoneEntity>> {
    const filters: MilestoneFilters = { status: MilestoneStatus.PENDING };
    if (projectId) {
      filters.projectId = projectId;
    }
    return this.findAll(ctx, filters, page, limit);
  }

  /**
   * Find overdue milestones
   */
  async findOverdue(
    ctx: ServiceContext,
    projectId?: string,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<MilestoneEntity>> {
    const skip = (page - 1) * limit;

    const qb = this.repository
      .createQueryBuilder('m')
      .where('m.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('m.date_deadline < :today', { today: new Date() })
      .andWhere('m.status = :status', { status: MilestoneStatus.PENDING });

    if (projectId) {
      qb.andWhere('m.project_id = :projectId', { projectId });
    }

    qb.orderBy('m.date_deadline', 'ASC').skip(skip).take(limit);

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

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

  /**
   * Find upcoming milestones (within next N days)
   */
  async findUpcoming(
    ctx: ServiceContext,
    days = 7,
    projectId?: string,
    page = 1,
    limit = 20
  ): Promise<PaginatedResult<MilestoneEntity>> {
    const skip = (page - 1) * limit;
    const futureDate = new Date();
    futureDate.setDate(futureDate.getDate() + days);

    const qb = this.repository
      .createQueryBuilder('m')
      .where('m.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('m.date_deadline >= :today', { today: new Date() })
      .andWhere('m.date_deadline <= :futureDate', { futureDate })
      .andWhere('m.status = :status', { status: MilestoneStatus.PENDING });

    if (projectId) {
      qb.andWhere('m.project_id = :projectId', { projectId });
    }

    qb.orderBy('m.date_deadline', 'ASC').skip(skip).take(limit);

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

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