/**
 * InspectionResultService - Servicio de resultados de inspección
 *
 * Gestiona el registro y consulta de resultados de items de inspección.
 *
 * @module Quality (MAI-009)
 */

import { Repository, FindOptionsWhere } from 'typeorm';
import { InspectionResult, InspectionResultStatus } from '../entities/inspection-result.entity';
import { Inspection } from '../entities/inspection.entity';
import { ChecklistItem } from '../entities/checklist-item.entity';

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

export interface RecordInspectionResultDto {
  checklistItemId: string;
  result: InspectionResultStatus;
  observations?: string;
  photoUrl?: string;
}

export interface UpdateInspectionResultDto {
  result?: InspectionResultStatus;
  observations?: string;
  photoUrl?: string;
}

export interface BatchRecordInspectionResultDto {
  items: RecordInspectionResultDto[];
}

export interface ResultFilters {
  inspectionId?: string;
  checklistItemId?: string;
  result?: InspectionResultStatus;
  hasObservations?: boolean;
  hasPhoto?: boolean;
}

export class InspectionResultService {
  constructor(
    private readonly resultRepository: Repository<InspectionResult>,
    private readonly inspectionRepository: Repository<Inspection>,
    private readonly checklistItemRepository: Repository<ChecklistItem>
  ) {}

  async findByInspection(ctx: ServiceContext, inspectionId: string): Promise<InspectionResult[]> {
    return this.resultRepository.find({
      where: {
        tenantId: ctx.tenantId,
        inspectionId,
      } as FindOptionsWhere<InspectionResult>,
      relations: ['checklistItem'],
      order: { createdAt: 'ASC' },
    });
  }

  async findById(ctx: ServiceContext, id: string): Promise<InspectionResult | null> {
    return this.resultRepository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<InspectionResult>,
      relations: ['checklistItem', 'inspection'],
    });
  }

  async findByInspectionAndItem(
    ctx: ServiceContext,
    inspectionId: string,
    checklistItemId: string
  ): Promise<InspectionResult | null> {
    return this.resultRepository.findOne({
      where: {
        tenantId: ctx.tenantId,
        inspectionId,
        checklistItemId,
      } as FindOptionsWhere<InspectionResult>,
      relations: ['checklistItem'],
    });
  }

  async recordResult(
    ctx: ServiceContext,
    inspectionId: string,
    dto: RecordInspectionResultDto
  ): Promise<InspectionResult> {
    // Validate inspection exists and is in valid state
    const inspection = await this.inspectionRepository.findOne({
      where: {
        id: inspectionId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Inspection>,
    });

    if (!inspection) {
      throw new Error('Inspection not found');
    }

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

    // Validate checklist item exists
    const item = await this.checklistItemRepository.findOne({
      where: {
        id: dto.checklistItemId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<ChecklistItem>,
    });

    if (!item) {
      throw new Error('Checklist item not found');
    }

    // Check if item requires photo
    if (item.requiresPhoto && dto.result === 'passed' && !dto.photoUrl) {
      throw new Error('This checklist item requires a photo for passed results');
    }

    // Check if result already exists
    let result = await this.findByInspectionAndItem(ctx, inspectionId, dto.checklistItemId);

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

    return this.resultRepository.save(result);
  }

  async recordBatchResults(
    ctx: ServiceContext,
    inspectionId: string,
    dto: BatchRecordInspectionResultDto
  ): Promise<InspectionResult[]> {
    // Validate inspection
    const inspection = await this.inspectionRepository.findOne({
      where: {
        id: inspectionId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Inspection>,
    });

    if (!inspection) {
      throw new Error('Inspection not found');
    }

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

    const results: InspectionResult[] = [];

    for (const item of dto.items) {
      const result = await this.recordResult(ctx, inspectionId, item);
      results.push(result);
    }

    return results;
  }

  async updateResult(
    ctx: ServiceContext,
    id: string,
    dto: UpdateInspectionResultDto
  ): Promise<InspectionResult | null> {
    const result = await this.findById(ctx, id);
    if (!result) {
      return null;
    }

    // Check inspection is still editable
    const inspection = await this.inspectionRepository.findOne({
      where: {
        id: result.inspectionId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Inspection>,
    });

    if (!inspection || inspection.status !== 'in_progress') {
      throw new Error('Cannot update results for non-in-progress inspections');
    }

    Object.assign(result, dto);
    result.inspectedAt = new Date();

    return this.resultRepository.save(result);
  }

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

    // Check inspection is still editable
    const inspection = await this.inspectionRepository.findOne({
      where: {
        id: result.inspectionId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Inspection>,
    });

    if (!inspection || inspection.status !== 'in_progress') {
      throw new Error('Cannot delete results for non-in-progress inspections');
    }

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

  async getInspectionSummary(ctx: ServiceContext, inspectionId: string): Promise<{
    totalItems: number;
    inspectedItems: number;
    passed: number;
    failed: number;
    notApplicable: number;
    pending: number;
    passRate: number;
    completionRate: number;
  }> {
    // Get inspection with checklist
    const inspection = await this.inspectionRepository.findOne({
      where: {
        id: inspectionId,
        tenantId: ctx.tenantId,
      } as FindOptionsWhere<Inspection>,
      relations: ['checklist', 'checklist.items'],
    });

    if (!inspection) {
      throw new Error('Inspection not found');
    }

    const totalItems = inspection.checklist?.items?.filter(i => i.isActive).length || 0;

    // Get results
    const results = await this.resultRepository.find({
      where: {
        tenantId: ctx.tenantId,
        inspectionId,
      } as FindOptionsWhere<InspectionResult>,
    });

    const passed = results.filter(r => r.result === 'passed').length;
    const failed = results.filter(r => r.result === 'failed').length;
    const notApplicable = results.filter(r => r.result === 'not_applicable').length;
    const pending = results.filter(r => r.result === 'pending').length;
    const inspectedItems = passed + failed + notApplicable;

    const applicableItems = inspectedItems - notApplicable;
    const passRate = applicableItems > 0 ? (passed / applicableItems) * 100 : 0;
    const completionRate = totalItems > 0 ? (inspectedItems / totalItems) * 100 : 0;

    return {
      totalItems,
      inspectedItems,
      passed,
      failed,
      notApplicable,
      pending,
      passRate: Math.round(passRate * 100) / 100,
      completionRate: Math.round(completionRate * 100) / 100,
    };
  }

  async getFailedItemsWithDetails(ctx: ServiceContext, inspectionId: string): Promise<InspectionResult[]> {
    return this.resultRepository.find({
      where: {
        tenantId: ctx.tenantId,
        inspectionId,
        result: 'failed' as InspectionResultStatus,
      } as FindOptionsWhere<InspectionResult>,
      relations: ['checklistItem'],
      order: { createdAt: 'ASC' },
    });
  }

  async getCriticalFailures(ctx: ServiceContext, inspectionId: string): Promise<InspectionResult[]> {
    return this.resultRepository
      .createQueryBuilder('ir')
      .leftJoinAndSelect('ir.checklistItem', 'item')
      .where('ir.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('ir.inspection_id = :inspectionId', { inspectionId })
      .andWhere('ir.result = :result', { result: 'failed' })
      .andWhere('item.is_critical = true')
      .orderBy('ir.created_at', 'ASC')
      .getMany();
  }

  async getResultsByCategory(ctx: ServiceContext, inspectionId: string): Promise<{
    category: string;
    total: number;
    passed: number;
    failed: number;
    passRate: number;
  }[]> {
    const results = await this.resultRepository
      .createQueryBuilder('ir')
      .leftJoinAndSelect('ir.checklistItem', 'item')
      .where('ir.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('ir.inspection_id = :inspectionId', { inspectionId })
      .andWhere('ir.result != :na', { na: 'not_applicable' })
      .getMany();

    // Group by category
    const categoryMap = new Map<string, { total: number; passed: number; failed: number }>();

    for (const result of results) {
      const category = result.checklistItem?.category || 'Unknown';
      if (!categoryMap.has(category)) {
        categoryMap.set(category, { total: 0, passed: 0, failed: 0 });
      }
      const stats = categoryMap.get(category)!;
      stats.total++;
      if (result.result === 'passed') stats.passed++;
      if (result.result === 'failed') stats.failed++;
    }

    return Array.from(categoryMap.entries()).map(([category, stats]) => ({
      category,
      total: stats.total,
      passed: stats.passed,
      failed: stats.failed,
      passRate: stats.total > 0 ? Math.round((stats.passed / stats.total) * 100 * 100) / 100 : 0,
    }));
  }

  async getResultsWithPhotos(ctx: ServiceContext, inspectionId: string): Promise<InspectionResult[]> {
    return this.resultRepository.find({
      where: {
        tenantId: ctx.tenantId,
        inspectionId,
      } as FindOptionsWhere<InspectionResult>,
      relations: ['checklistItem'],
      order: { createdAt: 'ASC' },
    }).then(results => results.filter(r => r.photoUrl));
  }
}
