/**
 * ChecklistService - Servicio de plantillas de inspección
 *
 * Gestión de checklists y sus items para inspecciones de calidad.
 *
 * @module Quality (MAI-009)
 */

import { Repository } from 'typeorm';
import { Checklist, ChecklistStage } from '../entities/checklist.entity';
import { ChecklistItem } from '../entities/checklist-item.entity';

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

export interface CreateChecklistDto {
  code: string;
  name: string;
  description?: string;
  stage: ChecklistStage;
  prototypeId?: string;
}

export interface UpdateChecklistDto {
  name?: string;
  description?: string;
  stage?: ChecklistStage;
  prototypeId?: string;
  isActive?: boolean;
}

export interface CreateChecklistItemDto {
  sequenceNumber: number;
  category: string;
  description: string;
  isCritical?: boolean;
  requiresPhoto?: boolean;
  acceptanceCriteria?: string;
}

export interface UpdateChecklistItemDto {
  sequenceNumber?: number;
  category?: string;
  description?: string;
  isCritical?: boolean;
  requiresPhoto?: boolean;
  acceptanceCriteria?: string;
  isActive?: boolean;
}

export interface ChecklistFilters {
  stage?: ChecklistStage;
  prototypeId?: string;
  isActive?: boolean;
  search?: string;
  page?: number;
  limit?: number;
}

export class ChecklistService {
  constructor(
    private readonly checklistRepository: Repository<Checklist>,
    private readonly itemRepository: Repository<ChecklistItem>,
  ) {}

  async findAll(
    ctx: ServiceContext,
    filters: ChecklistFilters = {},
  ): Promise<{ data: Checklist[]; total: number; page: number; limit: number }> {
    const page = filters.page || 1;
    const limit = filters.limit || 20;
    const skip = (page - 1) * limit;

    const queryBuilder = this.checklistRepository
      .createQueryBuilder('cl')
      .leftJoinAndSelect('cl.items', 'items', 'items.is_active = true')
      .where('cl.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('cl.deleted_at IS NULL');

    if (filters.stage) {
      queryBuilder.andWhere('cl.stage = :stage', { stage: filters.stage });
    }

    if (filters.prototypeId) {
      queryBuilder.andWhere('cl.prototype_id = :prototypeId', {
        prototypeId: filters.prototypeId,
      });
    }

    if (filters.isActive !== undefined) {
      queryBuilder.andWhere('cl.is_active = :isActive', { isActive: filters.isActive });
    }

    if (filters.search) {
      queryBuilder.andWhere(
        '(cl.name ILIKE :search OR cl.code ILIKE :search OR cl.description ILIKE :search)',
        { search: `%${filters.search}%` },
      );
    }

    const [data, total] = await queryBuilder
      .orderBy('cl.stage', 'ASC')
      .addOrderBy('cl.name', 'ASC')
      .skip(skip)
      .take(limit)
      .getManyAndCount();

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

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

  async findWithItems(ctx: ServiceContext, id: string): Promise<Checklist | null> {
    return this.checklistRepository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      },
      relations: ['items', 'createdBy'],
      order: { items: { sequenceNumber: 'ASC' } },
    });
  }

  async findByCode(ctx: ServiceContext, code: string): Promise<Checklist | null> {
    return this.checklistRepository.findOne({
      where: {
        code,
        tenantId: ctx.tenantId,
      },
    });
  }

  async findByStage(ctx: ServiceContext, stage: ChecklistStage): Promise<Checklist[]> {
    return this.checklistRepository
      .createQueryBuilder('cl')
      .leftJoinAndSelect('cl.items', 'items', 'items.is_active = true')
      .where('cl.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('cl.deleted_at IS NULL')
      .andWhere('cl.is_active = true')
      .andWhere('cl.stage = :stage', { stage })
      .orderBy('cl.name', 'ASC')
      .addOrderBy('items.sequence_number', 'ASC')
      .getMany();
  }

  async create(ctx: ServiceContext, dto: CreateChecklistDto): Promise<Checklist> {
    // Check code uniqueness
    const existing = await this.findByCode(ctx, dto.code);
    if (existing) {
      throw new Error(`Checklist with code ${dto.code} already exists`);
    }

    const checklist = this.checklistRepository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      code: dto.code,
      name: dto.name,
      description: dto.description,
      stage: dto.stage,
      prototypeId: dto.prototypeId,
      isActive: true,
      version: 1,
    });

    return this.checklistRepository.save(checklist);
  }

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

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

    return this.checklistRepository.save(checklist);
  }

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

    checklist.version += 1;
    if (ctx.userId) {
      checklist.updatedById = ctx.userId;
    }

    return this.checklistRepository.save(checklist);
  }

  async activate(ctx: ServiceContext, id: string): Promise<Checklist | null> {
    return this.update(ctx, id, { isActive: true });
  }

  async deactivate(ctx: ServiceContext, id: string): Promise<Checklist | null> {
    return this.update(ctx, id, { isActive: false });
  }

  async duplicate(ctx: ServiceContext, id: string, newCode: string, newName: string): Promise<Checklist | null> {
    const original = await this.findWithItems(ctx, id);
    if (!original) {
      return null;
    }

    // Check new code uniqueness
    const existing = await this.findByCode(ctx, newCode);
    if (existing) {
      throw new Error(`Checklist with code ${newCode} already exists`);
    }

    // Create new checklist
    const newChecklist = this.checklistRepository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      code: newCode,
      name: newName,
      description: original.description,
      stage: original.stage,
      prototypeId: original.prototypeId,
      isActive: true,
      version: 1,
    });

    const savedChecklist = await this.checklistRepository.save(newChecklist);

    // Duplicate items
    if (original.items && original.items.length > 0) {
      const newItems = original.items.map((item) =>
        this.itemRepository.create({
          tenantId: ctx.tenantId,
          checklistId: savedChecklist.id,
          sequenceNumber: item.sequenceNumber,
          category: item.category,
          description: item.description,
          isCritical: item.isCritical,
          requiresPhoto: item.requiresPhoto,
          acceptanceCriteria: item.acceptanceCriteria,
          isActive: true,
        }),
      );
      await this.itemRepository.save(newItems);
    }

    return this.findWithItems(ctx, savedChecklist.id);
  }

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

    const updateData: { deletedAt: Date; deletedById?: string } = { deletedAt: new Date() };
    if (ctx.userId) {
      updateData.deletedById = ctx.userId;
    }
    await this.checklistRepository.update(
      { id, tenantId: ctx.tenantId },
      updateData,
    );

    return true;
  }

  // Item management

  async addItem(
    ctx: ServiceContext,
    checklistId: string,
    dto: CreateChecklistItemDto,
  ): Promise<ChecklistItem> {
    const checklist = await this.findById(ctx, checklistId);
    if (!checklist) {
      throw new Error('Checklist not found');
    }

    const item = this.itemRepository.create({
      tenantId: ctx.tenantId,
      checklistId,
      sequenceNumber: dto.sequenceNumber,
      category: dto.category,
      description: dto.description,
      isCritical: dto.isCritical ?? false,
      requiresPhoto: dto.requiresPhoto ?? false,
      acceptanceCriteria: dto.acceptanceCriteria,
      isActive: true,
    });

    const savedItem = await this.itemRepository.save(item);

    // Increment checklist version
    await this.incrementVersion(ctx, checklistId);

    return savedItem;
  }

  async updateItem(
    ctx: ServiceContext,
    itemId: string,
    dto: UpdateChecklistItemDto,
  ): Promise<ChecklistItem | null> {
    const item = await this.itemRepository.findOne({
      where: { id: itemId, tenantId: ctx.tenantId },
    });

    if (!item) {
      return null;
    }

    Object.assign(item, dto);

    const savedItem = await this.itemRepository.save(item);

    // Increment checklist version
    await this.incrementVersion(ctx, item.checklistId);

    return savedItem;
  }

  async removeItem(ctx: ServiceContext, itemId: string): Promise<boolean> {
    const item = await this.itemRepository.findOne({
      where: { id: itemId, tenantId: ctx.tenantId },
    });

    if (!item) {
      return false;
    }

    await this.itemRepository.delete({ id: itemId, tenantId: ctx.tenantId });

    // Increment checklist version
    await this.incrementVersion(ctx, item.checklistId);

    return true;
  }

  async reorderItems(
    ctx: ServiceContext,
    checklistId: string,
    itemOrders: { itemId: string; sequenceNumber: number }[],
  ): Promise<boolean> {
    const checklist = await this.findById(ctx, checklistId);
    if (!checklist) {
      return false;
    }

    for (const order of itemOrders) {
      await this.itemRepository.update(
        { id: order.itemId, tenantId: ctx.tenantId, checklistId },
        { sequenceNumber: order.sequenceNumber },
      );
    }

    await this.incrementVersion(ctx, checklistId);

    return true;
  }

  async getStatistics(ctx: ServiceContext): Promise<{
    totalChecklists: number;
    activeChecklists: number;
    byStage: Record<string, number>;
    totalItems: number;
    criticalItems: number;
  }> {
    const checklists = await this.checklistRepository.find({
      where: { tenantId: ctx.tenantId, deletedAt: undefined },
      relations: ['items'],
    });

    const byStage: Record<string, number> = {};
    let totalItems = 0;
    let criticalItems = 0;

    for (const cl of checklists) {
      byStage[cl.stage] = (byStage[cl.stage] || 0) + 1;
      if (cl.items) {
        totalItems += cl.items.length;
        criticalItems += cl.items.filter((i) => i.isCritical).length;
      }
    }

    return {
      totalChecklists: checklists.length,
      activeChecklists: checklists.filter((c) => c.isActive).length,
      byStage,
      totalItems,
      criticalItems,
    };
  }
}
