/**
 * InspectionService - Servicio de inspecciones de calidad
 *
 * Gestión de inspecciones con checklists y resultados.
 *
 * @module Quality
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { Inspection, InspectionStatus } from '../entities/inspection.entity';
import { InspectionResult } from '../entities/inspection-result.entity';
import { NonConformity } from '../entities/non-conformity.entity';
import { Checklist } from '../entities/checklist.entity';
import { ServiceContext, PaginatedResult } from '../../../shared/services/base.service';

export interface CreateInspectionDto {
  checklistId: string;
  loteId: string;
  inspectionDate: Date;
  inspectorId: string;
  notes?: string;
}

export interface RecordResultDto {
  checklistItemId: string;
  result: 'passed' | 'failed' | 'not_applicable';
  observations?: string;
  photoUrl?: string;
}

export interface InspectionFilters {
  checklistId?: string;
  loteId?: string;
  inspectorId?: string;
  status?: InspectionStatus;
  dateFrom?: Date;
  dateTo?: Date;
}

export class InspectionService {
  constructor(
    private readonly inspectionRepository: Repository<Inspection>,
    private readonly resultRepository: Repository<InspectionResult>,
    private readonly nonConformityRepository: Repository<NonConformity>,
    private readonly checklistRepository: Repository<Checklist>
  ) {}

  private generateInspectionNumber(): string {
    const now = new Date();
    const year = now.getFullYear().toString().slice(-2);
    const month = (now.getMonth() + 1).toString().padStart(2, '0');
    const random = Math.random().toString(36).substring(2, 8).toUpperCase();
    return `INS-${year}${month}-${random}`;
  }

  private generateNCNumber(): string {
    const now = new Date();
    const year = now.getFullYear().toString().slice(-2);
    const month = (now.getMonth() + 1).toString().padStart(2, '0');
    const random = Math.random().toString(36).substring(2, 6).toUpperCase();
    return `NC-${year}${month}-${random}`;
  }

  async findWithFilters(
    ctx: ServiceContext,
    filters: InspectionFilters = {},
    page: number = 1,
    limit: number = 20
  ): Promise<PaginatedResult<Inspection>> {
    const skip = (page - 1) * limit;

    const queryBuilder = this.inspectionRepository
      .createQueryBuilder('insp')
      .leftJoinAndSelect('insp.checklist', 'checklist')
      .leftJoinAndSelect('insp.inspector', 'inspector')
      .where('insp.tenant_id = :tenantId', { tenantId: ctx.tenantId });

    if (filters.checklistId) {
      queryBuilder.andWhere('insp.checklist_id = :checklistId', { checklistId: filters.checklistId });
    }

    if (filters.loteId) {
      queryBuilder.andWhere('insp.lote_id = :loteId', { loteId: filters.loteId });
    }

    if (filters.inspectorId) {
      queryBuilder.andWhere('insp.inspector_id = :inspectorId', { inspectorId: filters.inspectorId });
    }

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

    if (filters.dateFrom) {
      queryBuilder.andWhere('insp.inspection_date >= :dateFrom', { dateFrom: filters.dateFrom });
    }

    if (filters.dateTo) {
      queryBuilder.andWhere('insp.inspection_date <= :dateTo', { dateTo: filters.dateTo });
    }

    queryBuilder
      .orderBy('insp.inspection_date', 'DESC')
      .skip(skip)
      .take(limit);

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

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

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

  async findWithDetails(ctx: ServiceContext, id: string): Promise<Inspection | null> {
    return this.inspectionRepository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      } as unknown as FindOptionsWhere<Inspection>,
      relations: ['checklist', 'checklist.items', 'inspector', 'results', 'results.checklistItem', 'nonConformities'],
    });
  }

  async create(ctx: ServiceContext, dto: CreateInspectionDto): Promise<Inspection> {
    // Validate checklist exists
    const checklist = await this.checklistRepository.findOne({
      where: {
        id: dto.checklistId,
        tenantId: ctx.tenantId,
        isActive: true,
        deletedAt: null,
      } as unknown as FindOptionsWhere<Checklist>,
      relations: ['items'],
    });

    if (!checklist) {
      throw new Error('Checklist not found or inactive');
    }

    const inspection = this.inspectionRepository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      checklistId: dto.checklistId,
      loteId: dto.loteId,
      inspectionNumber: this.generateInspectionNumber(),
      inspectionDate: dto.inspectionDate,
      inspectorId: dto.inspectorId,
      notes: dto.notes,
      status: 'pending',
      totalItems: checklist.items?.filter(i => i.isActive).length || 0,
    });

    return this.inspectionRepository.save(inspection);
  }

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

    if (inspection.status !== 'pending') {
      throw new Error('Can only start pending inspections');
    }

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

    return this.inspectionRepository.save(inspection);
  }

  async recordResult(ctx: ServiceContext, inspectionId: string, dto: RecordResultDto): Promise<InspectionResult> {
    const inspection = await this.findById(ctx, inspectionId);
    if (!inspection) {
      throw new Error('Inspection not found');
    }

    if (inspection.status !== 'in_progress') {
      throw new Error('Can only record results for in-progress inspections');
    }

    // Check if result already exists
    let result = await this.resultRepository.findOne({
      where: {
        inspectionId,
        checklistItemId: dto.checklistItemId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<InspectionResult>,
    });

    if (result) {
      // Update existing
      result.result = dto.result;
      result.observations = dto.observations || result.observations;
      result.photoUrl = dto.photoUrl || result.photoUrl;
      result.inspectedAt = new Date();
    } else {
      // Create new
      result = this.resultRepository.create({
        tenantId: ctx.tenantId,
        inspectionId,
        checklistItemId: dto.checklistItemId,
        result: dto.result,
        observations: dto.observations,
        photoUrl: dto.photoUrl,
        inspectedAt: new Date(),
      });
    }

    const savedResult = await this.resultRepository.save(result);

    // If failed, create non-conformity automatically
    if (dto.result === 'failed') {
      const nc = this.nonConformityRepository.create({
        tenantId: ctx.tenantId,
        createdById: ctx.userId,
        inspectionId,
        loteId: inspection.loteId,
        ncNumber: this.generateNCNumber(),
        detectionDate: new Date(),
        category: 'Inspection Finding',
        severity: 'minor',
        description: dto.observations || 'Failed inspection item',
        photoUrl: dto.photoUrl,
        status: 'open',
      });
      await this.nonConformityRepository.save(nc);
    }

    return savedResult;
  }

  async completeInspection(ctx: ServiceContext, id: string): Promise<Inspection | null> {
    const inspection = await this.findWithDetails(ctx, id);
    if (!inspection) {
      return null;
    }

    if (inspection.status !== 'in_progress') {
      throw new Error('Can only complete in-progress inspections');
    }

    // Calculate pass/fail counts
    const results = inspection.results || [];
    const passed = results.filter(r => r.result === 'passed').length;
    const failed = results.filter(r => r.result === 'failed').length;
    const total = results.filter(r => r.result !== 'not_applicable').length;

    inspection.passedItems = passed;
    inspection.failedItems = failed;
    inspection.passRate = total > 0 ? (passed / total) * 100 : 0;
    inspection.status = 'completed';
    inspection.completedAt = new Date();
    inspection.updatedById = ctx.userId || '';

    return this.inspectionRepository.save(inspection);
  }

  async approveInspection(ctx: ServiceContext, id: string): Promise<Inspection | null> {
    const inspection = await this.findWithDetails(ctx, id);
    if (!inspection) {
      return null;
    }

    if (inspection.status !== 'completed') {
      throw new Error('Can only approve completed inspections');
    }

    // Check for open critical non-conformities
    const criticalNCs = inspection.nonConformities?.filter(
      nc => nc.severity === 'critical' && nc.status !== 'verified'
    );

    if (criticalNCs && criticalNCs.length > 0) {
      throw new Error('Cannot approve inspection with open critical non-conformities');
    }

    inspection.status = 'approved';
    inspection.approvedById = ctx.userId || '';
    inspection.approvedAt = new Date();
    inspection.updatedById = ctx.userId || '';

    return this.inspectionRepository.save(inspection);
  }

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

    if (inspection.status !== 'completed') {
      throw new Error('Can only reject completed inspections');
    }

    inspection.status = 'rejected';
    inspection.rejectionReason = reason;
    inspection.updatedById = ctx.userId || '';

    return this.inspectionRepository.save(inspection);
  }
}
