/**
 * CorrectiveActionService - Servicio de acciones correctivas y preventivas (CAPA)
 *
 * Gestiona el ciclo de vida de acciones correctivas asociadas a no conformidades.
 *
 * @module Quality (MAI-009)
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { CorrectiveAction, ActionType, ActionStatus } from '../entities/corrective-action.entity';
import { NonConformity } from '../entities/non-conformity.entity';

interface ServiceContext {
  tenantId: string;
  userId?: string;
}

export interface CreateCorrectiveActionDto {
  nonConformityId: string;
  actionType: ActionType;
  description: string;
  responsibleId: string;
  dueDate: Date;
}

export interface UpdateCorrectiveActionDto {
  actionType?: ActionType;
  description?: string;
  responsibleId?: string;
  dueDate?: Date;
}

export interface CompleteActionDto {
  completionNotes: string;
}

export interface VerifyActionDto {
  effectivenessVerified: boolean;
  verificationNotes?: string;
}

export interface CorrectiveActionFilters {
  nonConformityId?: string;
  responsibleId?: string;
  actionType?: ActionType;
  status?: ActionStatus;
  effectivenessVerified?: boolean;
  dueDateFrom?: Date;
  dueDateTo?: Date;
  search?: string;
}

export class CorrectiveActionService {
  constructor(
    private readonly actionRepository: Repository<CorrectiveAction>,
    private readonly nonConformityRepository: Repository<NonConformity>
  ) {}

  async findWithFilters(
    ctx: ServiceContext,
    filters: CorrectiveActionFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<{ data: CorrectiveAction[]; total: number; page: number; limit: number }> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.actionRepository
      .createQueryBuilder('ca')
      .leftJoinAndSelect('ca.nonConformity', 'nc')
      .leftJoinAndSelect('ca.responsible', 'responsible')
      .leftJoinAndSelect('ca.createdBy', 'createdBy')
      .leftJoinAndSelect('ca.verifiedBy', 'verifiedBy')
      .where('ca.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.nonConformityId) {
      queryBuilder.andWhere('ca.no_conformidad_id = :nonConformityId', {
        nonConformityId: filters.nonConformityId,
      });
    }

    if (filters.responsibleId) {
      queryBuilder.andWhere('ca.responsible_id = :responsibleId', {
        responsibleId: filters.responsibleId,
      });
    }

    if (filters.actionType) {
      queryBuilder.andWhere('ca.action_type = :actionType', { actionType: filters.actionType });
    }

    if (filters.status) {
      queryBuilder.andWhere('ca.status = :status', { status: filters.status });
    }

    if (filters.effectivenessVerified !== undefined) {
      queryBuilder.andWhere('ca.effectiveness_verified = :effectivenessVerified', {
        effectivenessVerified: filters.effectivenessVerified,
      });
    }

    if (filters.dueDateFrom) {
      queryBuilder.andWhere('ca.due_date >= :dueDateFrom', { dueDateFrom: filters.dueDateFrom });
    }

    if (filters.dueDateTo) {
      queryBuilder.andWhere('ca.due_date <= :dueDateTo', { dueDateTo: filters.dueDateTo });
    }

    if (filters.search) {
      queryBuilder.andWhere('ca.description ILIKE :search', { search: `%${filters.search}%` });
    }

    queryBuilder
      .orderBy('ca.due_date', 'ASC')
      .addOrderBy('ca.created_at', 'DESC')
      .skip(skip)
      .take(limit);

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

    return { data, total, page, limit };
  }

  async findById(ctx: ServiceContext, id: string): Promise<CorrectiveAction | null> {
    return this.actionRepository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<CorrectiveAction>,
    });
  }

  async findWithDetails(ctx: ServiceContext, id: string): Promise<CorrectiveAction | null> {
    return this.actionRepository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<CorrectiveAction>,
      relations: ['nonConformity', 'responsible', 'createdBy', 'verifiedBy'],
    });
  }

  async findByNonConformity(ctx: ServiceContext, nonConformityId: string): Promise<CorrectiveAction[]> {
    return this.actionRepository.find({
      where: {
        tenantId: ctx.tenantId,
        nonConformityId,
      } as FindOptionsWhere<CorrectiveAction>,
      relations: ['responsible', 'verifiedBy'],
      order: { dueDate: 'ASC' },
    });
  }

  async create(ctx: ServiceContext, dto: CreateCorrectiveActionDto): Promise<CorrectiveAction> {
    // Validate non-conformity exists
    const nc = await this.nonConformityRepository.findOne({
      where: {
        id: dto.nonConformityId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<NonConformity>,
    });

    if (!nc) {
      throw new Error('Non-conformity not found');
    }

    if (nc.status === 'verified') {
      throw new Error('Cannot add corrective actions to verified non-conformities');
    }

    const action = this.actionRepository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      nonConformityId: dto.nonConformityId,
      actionType: dto.actionType,
      description: dto.description,
      responsibleId: dto.responsibleId,
      dueDate: dto.dueDate,
      status: 'pending',
      effectivenessVerified: false,
    });

    const savedAction = await this.actionRepository.save(action);

    // Update non-conformity status to in_progress if it's open
    if (nc.status === 'open') {
      nc.status = 'in_progress';
      nc.updatedById = ctx.userId || '';
      await this.nonConformityRepository.save(nc);
    }

    return savedAction;
  }

  async update(ctx: ServiceContext, id: string, dto: UpdateCorrectiveActionDto): Promise<CorrectiveAction | null> {
    const action = await this.findById(ctx, id);
    if (!action) {
      return null;
    }

    if (action.status === 'completed' || action.status === 'verified') {
      throw new Error('Cannot update completed or verified actions');
    }

    Object.assign(action, {
      ...dto,
      updatedById: ctx.userId,
    });

    return this.actionRepository.save(action);
  }

  async startWork(ctx: ServiceContext, id: string): Promise<CorrectiveAction | null> {
    const action = await this.findById(ctx, id);
    if (!action) {
      return null;
    }

    if (action.status !== 'pending') {
      throw new Error('Can only start work on pending actions');
    }

    action.status = 'in_progress';
    action.updatedById = ctx.userId || '';

    return this.actionRepository.save(action);
  }

  async complete(ctx: ServiceContext, id: string, dto: CompleteActionDto): Promise<CorrectiveAction | null> {
    const action = await this.findById(ctx, id);
    if (!action) {
      return null;
    }

    if (action.status !== 'pending' && action.status !== 'in_progress') {
      throw new Error('Can only complete pending or in-progress actions');
    }

    action.status = 'completed';
    action.completedAt = new Date();
    action.completionNotes = dto.completionNotes;
    action.updatedById = ctx.userId || '';

    return this.actionRepository.save(action);
  }

  async verify(ctx: ServiceContext, id: string, dto: VerifyActionDto): Promise<CorrectiveAction | null> {
    const action = await this.findWithDetails(ctx, id);
    if (!action) {
      return null;
    }

    if (action.status !== 'completed') {
      throw new Error('Can only verify completed actions');
    }

    action.status = 'verified';
    action.verifiedAt = new Date();
    action.verifiedById = ctx.userId || '';
    action.effectivenessVerified = dto.effectivenessVerified;
    if (dto.verificationNotes) {
      action.completionNotes = `${action.completionNotes || ''}\n\n[VERIFICATION]: ${dto.verificationNotes}`;
    }
    action.updatedById = ctx.userId || '';

    const savedAction = await this.actionRepository.save(action);

    // Check if all actions for the NC are verified
    await this.checkAndCloseNonConformity(ctx, action.nonConformityId);

    return savedAction;
  }

  async reopen(ctx: ServiceContext, id: string, reason: string): Promise<CorrectiveAction | null> {
    const action = await this.findById(ctx, id);
    if (!action) {
      return null;
    }

    if (action.status !== 'completed' && action.status !== 'verified') {
      throw new Error('Can only reopen completed or verified actions');
    }

    action.status = 'in_progress';
    action.completionNotes = `${action.completionNotes || ''}\n\n[REOPENED]: ${reason}`;
    action.verifiedAt = null as unknown as Date;
    action.verifiedById = null as unknown as string;
    action.effectivenessVerified = false;
    action.updatedById = ctx.userId || '';

    return this.actionRepository.save(action);
  }

  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const action = await this.findById(ctx, id);
    if (!action) {
      return false;
    }

    if (action.status !== 'pending') {
      throw new Error('Can only delete pending actions');
    }

    await this.actionRepository.delete({ id, tenantId: ctx.tenantId });
    return true;
  }

  async getOverdueActions(ctx: ServiceContext): Promise<CorrectiveAction[]> {
    return this.actionRepository
      .createQueryBuilder('ca')
      .leftJoinAndSelect('ca.nonConformity', 'nc')
      .leftJoinAndSelect('ca.responsible', 'responsible')
      .where('ca.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('ca.status IN (:...statuses)', { statuses: ['pending', 'in_progress'] })
      .andWhere('ca.due_date < :now', { now: new Date() })
      .orderBy('ca.due_date', 'ASC')
      .getMany();
  }

  async getStatsByResponsible(ctx: ServiceContext, responsibleId: string): Promise<{
    total: number;
    pending: number;
    inProgress: number;
    completed: number;
    verified: number;
    overdue: number;
    effectivenessRate: number;
  }> {
    const actions = await this.actionRepository.find({
      where: {
        tenantId: ctx.tenantId,
        responsibleId,
      } as FindOptionsWhere<CorrectiveAction>,
    });

    const now = new Date();
    const total = actions.length;
    const pending = actions.filter(a => a.status === 'pending').length;
    const inProgress = actions.filter(a => a.status === 'in_progress').length;
    const completed = actions.filter(a => a.status === 'completed').length;
    const verified = actions.filter(a => a.status === 'verified').length;
    const overdue = actions.filter(
      a => (a.status === 'pending' || a.status === 'in_progress') && a.dueDate < now
    ).length;
    const effectiveCount = actions.filter(a => a.effectivenessVerified).length;
    const effectivenessRate = verified > 0 ? (effectiveCount / verified) * 100 : 0;

    return { total, pending, inProgress, completed, verified, overdue, effectivenessRate };
  }

  private async checkAndCloseNonConformity(ctx: ServiceContext, nonConformityId: string): Promise<void> {
    const actions = await this.actionRepository.find({
      where: {
        tenantId: ctx.tenantId,
        nonConformityId,
      } as FindOptionsWhere<CorrectiveAction>,
    });

    // If all actions are verified, close the NC
    const allVerified = actions.length > 0 && actions.every(a => a.status === 'verified');
    const allEffective = actions.every(a => a.effectivenessVerified);

    if (allVerified && allEffective) {
      const nc = await this.nonConformityRepository.findOne({
        where: {
          id: nonConformityId,
          tenantId: ctx.tenantId,
        } as FindOptionsWhere<NonConformity>,
      });

      if (nc && nc.status === 'in_progress') {
        nc.status = 'closed';
        nc.closedAt = new Date();
        nc.closedById = ctx.userId || '';
        nc.updatedById = ctx.userId || '';
        await this.nonConformityRepository.save(nc);
      }
    }
  }
}
