/**
 * PurchaseOrderConstructionService - Servicio de extensión de órdenes de compra
 *
 * Gestión de datos adicionales de OC para construcción.
 *
 * @module Purchase (MAI-004)
 */

import { Repository } from 'typeorm';
import { PurchaseOrderConstruction } from '../entities/purchase-order-construction.entity';

export interface CreatePurchaseOrderConstructionDto {
  purchaseOrderId: string;
  fraccionamientoId?: string;
  requisicionId?: string;
  deliveryLocation?: string;
  deliveryContact?: string;
  deliveryPhone?: string;
}

export interface UpdatePurchaseOrderConstructionDto {
  fraccionamientoId?: string;
  requisicionId?: string;
  deliveryLocation?: string;
  deliveryContact?: string;
  deliveryPhone?: string;
}

export interface RegisterReceptionDto {
  receivedById: string;
  qualityApproved?: boolean;
  qualityNotes?: string;
}

export interface PurchaseOrderConstructionFilters {
  fraccionamientoId?: string;
  requisicionId?: string;
  qualityApproved?: boolean;
  receivedFrom?: Date;
  receivedTo?: Date;
  page?: number;
  limit?: number;
}

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

export class PurchaseOrderConstructionService {
  constructor(
    private readonly repository: Repository<PurchaseOrderConstruction>,
  ) {}

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

    const queryBuilder = this.repository
      .createQueryBuilder('poc')
      .leftJoinAndSelect('poc.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('poc.requisicion', 'requisicion')
      .leftJoinAndSelect('poc.receivedBy', 'receivedBy')
      .where('poc.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('poc.deleted_at IS NULL');

    if (filters.fraccionamientoId) {
      queryBuilder.andWhere('poc.fraccionamiento_id = :fraccionamientoId', {
        fraccionamientoId: filters.fraccionamientoId,
      });
    }

    if (filters.requisicionId) {
      queryBuilder.andWhere('poc.requisicion_id = :requisicionId', {
        requisicionId: filters.requisicionId,
      });
    }

    if (filters.qualityApproved !== undefined) {
      queryBuilder.andWhere('poc.quality_approved = :qualityApproved', {
        qualityApproved: filters.qualityApproved,
      });
    }

    if (filters.receivedFrom) {
      queryBuilder.andWhere('poc.received_at >= :receivedFrom', {
        receivedFrom: filters.receivedFrom,
      });
    }

    if (filters.receivedTo) {
      queryBuilder.andWhere('poc.received_at <= :receivedTo', {
        receivedTo: filters.receivedTo,
      });
    }

    const [data, total] = await queryBuilder
      .orderBy('poc.created_at', 'DESC')
      .skip(skip)
      .take(limit)
      .getManyAndCount();

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

  async findById(ctx: ServiceContext, id: string): Promise<PurchaseOrderConstruction | null> {
    return this.repository.findOne({
      where: {
        id,
        tenantId: ctx.tenantId,
      },
      relations: ['fraccionamiento', 'requisicion', 'receivedBy', 'createdBy'],
    });
  }

  async findByPurchaseOrderId(
    ctx: ServiceContext,
    purchaseOrderId: string,
  ): Promise<PurchaseOrderConstruction | null> {
    return this.repository.findOne({
      where: {
        purchaseOrderId,
        tenantId: ctx.tenantId,
      },
      relations: ['fraccionamiento', 'requisicion'],
    });
  }

  async create(
    ctx: ServiceContext,
    dto: CreatePurchaseOrderConstructionDto,
  ): Promise<PurchaseOrderConstruction> {
    // Check if extension already exists for this purchase order
    const existing = await this.findByPurchaseOrderId(ctx, dto.purchaseOrderId);
    if (existing) {
      throw new Error('Construction extension already exists for this purchase order');
    }

    const extension = this.repository.create({
      tenantId: ctx.tenantId,
      createdById: ctx.userId,
      purchaseOrderId: dto.purchaseOrderId,
      fraccionamientoId: dto.fraccionamientoId,
      requisicionId: dto.requisicionId,
      deliveryLocation: dto.deliveryLocation,
      deliveryContact: dto.deliveryContact,
      deliveryPhone: dto.deliveryPhone,
    });

    return this.repository.save(extension);
  }

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

    // Cannot update if already received
    if (extension.receivedAt) {
      throw new Error('Cannot update extension after order has been received');
    }

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

    return this.repository.save(extension);
  }

  async registerReception(
    ctx: ServiceContext,
    id: string,
    dto: RegisterReceptionDto,
  ): Promise<PurchaseOrderConstruction | null> {
    const extension = await this.findById(ctx, id);
    if (!extension) {
      return null;
    }

    if (extension.receivedAt) {
      throw new Error('Order has already been received');
    }

    extension.receivedById = dto.receivedById;
    extension.receivedAt = new Date();
    if (dto.qualityApproved !== undefined) {
      extension.qualityApproved = dto.qualityApproved;
    }
    if (dto.qualityNotes) {
      extension.qualityNotes = dto.qualityNotes;
    }
    if (ctx.userId) {
      extension.updatedById = ctx.userId;
    }

    return this.repository.save(extension);
  }

  async updateQualityStatus(
    ctx: ServiceContext,
    id: string,
    approved: boolean,
    notes?: string,
  ): Promise<PurchaseOrderConstruction | null> {
    const extension = await this.findById(ctx, id);
    if (!extension) {
      return null;
    }

    if (!extension.receivedAt) {
      throw new Error('Cannot update quality status before reception');
    }

    extension.qualityApproved = approved;
    if (notes) {
      extension.qualityNotes = notes;
    }
    if (ctx.userId) {
      extension.updatedById = ctx.userId;
    }

    return this.repository.save(extension);
  }

  async findPendingReception(
    ctx: ServiceContext,
    fraccionamientoId?: string,
  ): Promise<PurchaseOrderConstruction[]> {
    const queryBuilder = this.repository
      .createQueryBuilder('poc')
      .leftJoinAndSelect('poc.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('poc.requisicion', 'requisicion')
      .where('poc.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('poc.deleted_at IS NULL')
      .andWhere('poc.received_at IS NULL');

    if (fraccionamientoId) {
      queryBuilder.andWhere('poc.fraccionamiento_id = :fraccionamientoId', {
        fraccionamientoId,
      });
    }

    return queryBuilder.orderBy('poc.created_at', 'ASC').getMany();
  }

  async findPendingQualityApproval(ctx: ServiceContext): Promise<PurchaseOrderConstruction[]> {
    return this.repository
      .createQueryBuilder('poc')
      .leftJoinAndSelect('poc.fraccionamiento', 'fraccionamiento')
      .leftJoinAndSelect('poc.receivedBy', 'receivedBy')
      .where('poc.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('poc.deleted_at IS NULL')
      .andWhere('poc.received_at IS NOT NULL')
      .andWhere('poc.quality_approved IS NULL')
      .orderBy('poc.received_at', 'ASC')
      .getMany();
  }

  async getReceptionStatistics(
    ctx: ServiceContext,
    fraccionamientoId?: string,
    dateFrom?: Date,
    dateTo?: Date,
  ): Promise<{
    totalOrders: number;
    receivedOrders: number;
    pendingOrders: number;
    qualityApproved: number;
    qualityRejected: number;
    qualityPending: number;
  }> {
    const queryBuilder = this.repository
      .createQueryBuilder('poc')
      .where('poc.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('poc.deleted_at IS NULL');

    if (fraccionamientoId) {
      queryBuilder.andWhere('poc.fraccionamiento_id = :fraccionamientoId', {
        fraccionamientoId,
      });
    }

    if (dateFrom) {
      queryBuilder.andWhere('poc.created_at >= :dateFrom', { dateFrom });
    }

    if (dateTo) {
      queryBuilder.andWhere('poc.created_at <= :dateTo', { dateTo });
    }

    const all = await queryBuilder.getMany();

    const received = all.filter((o) => o.receivedAt);
    const pending = all.filter((o) => !o.receivedAt);
    const qualityApproved = received.filter((o) => o.qualityApproved === true);
    const qualityRejected = received.filter((o) => o.qualityApproved === false);
    const qualityPending = received.filter((o) => o.qualityApproved === null);

    return {
      totalOrders: all.length,
      receivedOrders: received.length,
      pendingOrders: pending.length,
      qualityApproved: qualityApproved.length,
      qualityRejected: qualityRejected.length,
      qualityPending: qualityPending.length,
    };
  }

  async softDelete(ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.repository.update(
      { id, tenantId: ctx.tenantId },
      { deletedAt: new Date(), deletedById: ctx.userId },
    );
    return (result.affected ?? 0) > 0;
  }
}
