/**
 * PrototipoService - Gestión de prototipos de vivienda
 *
 * CRUD de prototipos con soporte multi-tenant.
 *
 * @module Construction
 */

import { Repository, IsNull } from 'typeorm';
import { Prototipo } from '../entities/prototipo.entity';

export interface CreatePrototipoDto {
  code: string;
  name: string;
  description?: string;
  type?: string;
  areaConstructionM2?: number;
  areaTerrainM2?: number;
  bedrooms?: number;
  bathrooms?: number;
  parkingSpaces?: number;
  floors?: number;
  basePrice?: number;
  blueprintUrl?: string;
  renderUrl?: string;
  metadata?: Record<string, unknown>;
}

export interface UpdatePrototipoDto extends Partial<CreatePrototipoDto> {
  isActive?: boolean;
}

export interface PrototipoListOptions {
  tenantId: string;
  page?: number;
  limit?: number;
  search?: string;
  type?: string;
  isActive?: boolean;
}

export class PrototipoService {
  constructor(private readonly repository: Repository<Prototipo>) {}

  /**
   * Listar prototipos
   */
  async findAll(options: PrototipoListOptions): Promise<{ items: Prototipo[]; total: number }> {
    const { tenantId, page = 1, limit = 20, search, type, isActive } = options;

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

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

    if (type) {
      query.andWhere('p.type = :type', { type });
    }

    if (isActive !== undefined) {
      query.andWhere('p.is_active = :isActive', { isActive });
    }

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

    return { items, total };
  }

  /**
   * Obtener prototipo por ID
   */
  async findById(id: string, tenantId: string): Promise<Prototipo | null> {
    return this.repository.findOne({
      where: { id, tenantId, deletedAt: IsNull() } as any,
    });
  }

  /**
   * Obtener prototipo por código
   */
  async findByCode(code: string, tenantId: string): Promise<Prototipo | null> {
    return this.repository.findOne({
      where: { code, tenantId, deletedAt: IsNull() } as any,
    });
  }

  /**
   * Crear prototipo
   */
  async create(tenantId: string, dto: CreatePrototipoDto, createdBy?: string): Promise<Prototipo> {
    const existing = await this.findByCode(dto.code, tenantId);
    if (existing) {
      throw new Error('Prototype code already exists');
    }

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

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

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

    // Exclude metadata from spread to avoid TypeORM type issues
    const { metadata, ...updateData } = dto;
    await this.repository.update(id, {
      ...updateData,
      ...(metadata && { metadata: metadata as any }),
      updatedBy,
      updatedAt: new Date(),
    });

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

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

    // TODO: Verificar si está asignado a lotes antes de eliminar

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

  /**
   * Activar/Desactivar prototipo
   */
  async setActive(id: string, tenantId: string, isActive: boolean): Promise<Prototipo> {
    const prototipo = await this.findById(id, tenantId);
    if (!prototipo) {
      throw new Error('Prototype not found');
    }

    await this.repository.update(id, { isActive });
    return this.findById(id, tenantId) as Promise<Prototipo>;
  }
}
