/**
 * ManzanaService - Gestión de manzanas (bloques)
 *
 * CRUD de manzanas con soporte multi-tenant.
 *
 * @module Construction
 */

import { Repository, IsNull } from 'typeorm';
import { Manzana } from '../entities/manzana.entity';

export interface CreateManzanaDto {
  etapaId: string;
  code: string;
  name?: string;
  totalLots?: number;
}

export interface UpdateManzanaDto extends Partial<CreateManzanaDto> {}

export interface ManzanaListOptions {
  tenantId: string;
  etapaId?: string;
  page?: number;
  limit?: number;
  search?: string;
}

export class ManzanaService {
  constructor(private readonly repository: Repository<Manzana>) {}

  /**
   * Listar manzanas
   */
  async findAll(options: ManzanaListOptions): Promise<{ items: Manzana[]; total: number }> {
    const { tenantId, etapaId, page = 1, limit = 20, search } = options;

    const query = this.repository
      .createQueryBuilder('m')
      .where('m.tenant_id = :tenantId', { tenantId })
      .andWhere('m.deleted_at IS NULL');

    if (etapaId) {
      query.andWhere('m.etapa_id = :etapaId', { etapaId });
    }

    if (search) {
      query.andWhere('(m.code ILIKE :search OR m.name ILIKE :search)', { search: `%${search}%` });
    }

    const total = await query.getCount();
    const items = await query
      .skip((page - 1) * limit)
      .take(limit)
      .orderBy('m.code', 'ASC')
      .getMany();

    return { items, total };
  }

  /**
   * Obtener manzana por ID
   */
  async findById(id: string, tenantId: string): Promise<Manzana | null> {
    return this.repository.findOne({
      where: { id, tenantId, deletedAt: IsNull() } as any,
      relations: ['lotes'],
    });
  }

  /**
   * Obtener manzana por código dentro de una etapa
   */
  async findByCode(code: string, etapaId: string, tenantId: string): Promise<Manzana | null> {
    return this.repository.findOne({
      where: { code, etapaId, tenantId, deletedAt: IsNull() } as any,
    });
  }

  /**
   * Crear manzana
   */
  async create(tenantId: string, dto: CreateManzanaDto, createdBy?: string): Promise<Manzana> {
    const existing = await this.findByCode(dto.code, dto.etapaId, tenantId);
    if (existing) {
      throw new Error('Block code already exists in this stage');
    }

    return this.repository.save(
      this.repository.create({
        tenantId,
        ...dto,
        createdBy,
      })
    );
  }

  /**
   * Actualizar manzana
   */
  async update(id: string, tenantId: string, dto: UpdateManzanaDto, updatedBy?: string): Promise<Manzana> {
    const manzana = await this.findById(id, tenantId);
    if (!manzana) {
      throw new Error('Block not found');
    }

    // Verificar código único si se está cambiando
    if (dto.code && dto.code !== manzana.code) {
      const existing = await this.findByCode(dto.code, manzana.etapaId, tenantId);
      if (existing) {
        throw new Error('Block code already exists in this stage');
      }
    }

    await this.repository.update(id, {
      ...dto,
      updatedBy,
      updatedAt: new Date(),
    });

    return this.findById(id, tenantId) as Promise<Manzana>;
  }

  /**
   * Eliminar manzana (soft delete)
   */
  async delete(id: string, tenantId: string, _deletedBy?: string): Promise<void> {
    const manzana = await this.findById(id, tenantId);
    if (!manzana) {
      throw new Error('Block not found');
    }

    // TODO: Verificar si tiene lotes antes de eliminar

    await this.repository.update(id, {
      deletedAt: new Date(),
    });
  }

  /**
   * Obtener manzanas por etapa
   */
  async findByEtapa(etapaId: string, tenantId: string): Promise<Manzana[]> {
    return this.repository.find({
      where: { etapaId, tenantId, deletedAt: IsNull() } as any,
      order: { code: 'ASC' },
    });
  }
}
