/**
 * Warehouse Service
 * ERP Construccion - Modulo Warehouses
 *
 * Logica de negocio para gestion de almacenes.
 * Soporta multi-tenant via ServiceContext.
 */

import { Repository, DataSource, IsNull } from 'typeorm';
import { Warehouse } from '../entities/warehouse.entity';
import { PaginationOptions, PaginatedResult, ServiceContext } from './types';

// DTOs
export interface CreateWarehouseDto {
  code: string;
  name: string;
  description?: string;
  warehouseType?: 'standard' | 'transit' | 'returns' | 'quarantine' | 'virtual';
  branchId?: string;
  companyId?: string;
  addressLine1?: string;
  addressLine2?: string;
  city?: string;
  state?: string;
  postalCode?: string;
  country?: string;
  managerName?: string;
  phone?: string;
  email?: string;
  latitude?: number;
  longitude?: number;
  capacityUnits?: number;
  capacityVolume?: number;
  capacityWeight?: number;
  settings?: {
    allowNegative?: boolean;
    autoReorder?: boolean;
  };
}

export interface UpdateWarehouseDto extends Partial<CreateWarehouseDto> {
  isActive?: boolean;
  isDefault?: boolean;
}

export interface WarehouseFilters {
  warehouseType?: 'standard' | 'transit' | 'returns' | 'quarantine' | 'virtual';
  branchId?: string;
  companyId?: string;
  isActive?: boolean;
  search?: string;
}

export class WarehouseService {
  private warehouseRepository: Repository<Warehouse>;

  constructor(dataSource: DataSource) {
    this.warehouseRepository = dataSource.getRepository(Warehouse);
  }

  /**
   * Create a new warehouse
   */
  async create(ctx: ServiceContext, dto: CreateWarehouseDto): Promise<Warehouse> {
    // Check code uniqueness within company
    const companyIdValue = dto.companyId || ctx.companyId || undefined;
    const whereClause: any = {
      tenantId: ctx.tenantId,
      code: dto.code,
    };
    if (companyIdValue) {
      whereClause.companyId = companyIdValue;
    }

    const existing = await this.warehouseRepository.findOne({
      where: whereClause,
    });

    if (existing) {
      throw new Error(`Warehouse with code ${dto.code} already exists`);
    }

    const warehouse = this.warehouseRepository.create({
      tenantId: ctx.tenantId,
      companyId: dto.companyId || ctx.companyId,
      ...dto,
      warehouseType: dto.warehouseType || 'standard',
      createdBy: ctx.userId,
    });

    return this.warehouseRepository.save(warehouse);
  }

  /**
   * Find warehouse by ID
   */
  async findById(ctx: ServiceContext, id: string): Promise<Warehouse | null> {
    return this.warehouseRepository.findOne({
      where: { id, tenantId: ctx.tenantId, deletedAt: IsNull() },
      relations: ['company'],
    });
  }

  /**
   * Find warehouse by code
   */
  async findByCode(ctx: ServiceContext, code: string): Promise<Warehouse | null> {
    const where: any = {
      tenantId: ctx.tenantId,
      code,
      deletedAt: IsNull(),
    };

    if (ctx.companyId) {
      where.companyId = ctx.companyId;
    }

    return this.warehouseRepository.findOne({
      where,
      relations: ['company'],
    });
  }

  /**
   * Find default warehouse for tenant/company
   */
  async findDefault(ctx: ServiceContext): Promise<Warehouse | null> {
    const where: any = {
      tenantId: ctx.tenantId,
      isDefault: true,
      isActive: true,
      deletedAt: IsNull(),
    };

    if (ctx.companyId) {
      where.companyId = ctx.companyId;
    }

    return this.warehouseRepository.findOne({ where });
  }

  /**
   * List warehouses with filters and pagination
   */
  async findAll(
    ctx: ServiceContext,
    filters: WarehouseFilters = {},
    pagination: PaginationOptions = { page: 1, limit: 20 }
  ): Promise<PaginatedResult<Warehouse>> {
    const queryBuilder = this.warehouseRepository
      .createQueryBuilder('warehouse')
      .leftJoinAndSelect('warehouse.company', 'company')
      .where('warehouse.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('warehouse.deleted_at IS NULL');

    if (ctx.companyId) {
      queryBuilder.andWhere('warehouse.company_id = :companyId', { companyId: ctx.companyId });
    }

    if (filters.warehouseType) {
      queryBuilder.andWhere('warehouse.warehouse_type = :warehouseType', {
        warehouseType: filters.warehouseType,
      });
    }
    if (filters.branchId) {
      queryBuilder.andWhere('warehouse.branch_id = :branchId', { branchId: filters.branchId });
    }
    if (filters.companyId) {
      queryBuilder.andWhere('warehouse.company_id = :filterCompanyId', {
        filterCompanyId: filters.companyId,
      });
    }
    if (filters.isActive !== undefined) {
      queryBuilder.andWhere('warehouse.is_active = :isActive', { isActive: filters.isActive });
    }
    if (filters.search) {
      queryBuilder.andWhere(
        '(warehouse.code ILIKE :search OR warehouse.name ILIKE :search OR warehouse.city ILIKE :search)',
        { search: `%${filters.search}%` }
      );
    }

    const skip = (pagination.page - 1) * pagination.limit;

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

    return {
      data,
      total,
      page: pagination.page,
      limit: pagination.limit,
      totalPages: Math.ceil(total / pagination.limit),
    };
  }

  /**
   * Get all active warehouses (for dropdowns)
   */
  async getActiveWarehouses(ctx: ServiceContext): Promise<Warehouse[]> {
    const where: any = {
      tenantId: ctx.tenantId,
      isActive: true,
      deletedAt: IsNull(),
    };

    if (ctx.companyId) {
      where.companyId = ctx.companyId;
    }

    return this.warehouseRepository.find({
      where,
      order: { name: 'ASC' },
      select: ['id', 'code', 'name', 'warehouseType', 'isDefault'],
    });
  }

  /**
   * Update warehouse
   */
  async update(ctx: ServiceContext, id: string, dto: UpdateWarehouseDto): Promise<Warehouse | null> {
    const warehouse = await this.findById(ctx, id);
    if (!warehouse) return null;

    // If setting as default, unset other defaults first
    if (dto.isDefault === true) {
      const updateWhere: any = { tenantId: ctx.tenantId, isDefault: true };
      if (warehouse.companyId) {
        updateWhere.companyId = warehouse.companyId;
      }
      await this.warehouseRepository.update(updateWhere, { isDefault: false });
    }

    Object.assign(warehouse, dto, { updatedBy: ctx.userId });
    return this.warehouseRepository.save(warehouse);
  }

  /**
   * Set warehouse as default
   */
  async setAsDefault(ctx: ServiceContext, id: string): Promise<Warehouse | null> {
    return this.update(ctx, id, { isDefault: true });
  }

  /**
   * Activate/Deactivate warehouse
   */
  async setActive(ctx: ServiceContext, id: string, isActive: boolean): Promise<Warehouse | null> {
    return this.update(ctx, id, { isActive });
  }

  /**
   * Soft delete warehouse
   */
  async delete(ctx: ServiceContext, id: string): Promise<boolean> {
    const result = await this.warehouseRepository.update(
      { id, tenantId: ctx.tenantId },
      { deletedAt: new Date(), isActive: false, updatedBy: ctx.userId }
    );
    return (result.affected ?? 0) > 0;
  }

  /**
   * Get warehouse statistics
   */
  async getStatistics(ctx: ServiceContext): Promise<{
    total: number;
    active: number;
    inactive: number;
    byType: Record<string, number>;
    totalCapacityUnits: number;
  }> {
    const baseWhere: any = { tenantId: ctx.tenantId, deletedAt: IsNull() };
    if (ctx.companyId) {
      baseWhere.companyId = ctx.companyId;
    }

    const [total, active, byTypeRaw, capacityResult] = await Promise.all([
      this.warehouseRepository.count({ where: baseWhere }),
      this.warehouseRepository.count({ where: { ...baseWhere, isActive: true } }),

      this.warehouseRepository
        .createQueryBuilder('warehouse')
        .select('warehouse.warehouse_type', 'type')
        .addSelect('COUNT(*)', 'count')
        .where('warehouse.tenant_id = :tenantId', { tenantId: ctx.tenantId })
        .andWhere('warehouse.deleted_at IS NULL')
        .andWhere(ctx.companyId ? 'warehouse.company_id = :companyId' : '1=1', {
          companyId: ctx.companyId,
        })
        .groupBy('warehouse.warehouse_type')
        .getRawMany(),

      this.warehouseRepository
        .createQueryBuilder('warehouse')
        .select('SUM(warehouse.capacity_units)', 'total')
        .where('warehouse.tenant_id = :tenantId', { tenantId: ctx.tenantId })
        .andWhere('warehouse.deleted_at IS NULL')
        .andWhere('warehouse.is_active = true')
        .andWhere(ctx.companyId ? 'warehouse.company_id = :companyId' : '1=1', {
          companyId: ctx.companyId,
        })
        .getRawOne(),
    ]);

    const byType: Record<string, number> = {};
    byTypeRaw.forEach((row: { type: string; count: string }) => {
      byType[row.type] = parseInt(row.count, 10);
    });

    return {
      total,
      active,
      inactive: total - active,
      byType,
      totalCapacityUnits: parseInt(capacityResult?.total) || 0,
    };
  }

  /**
   * Search warehouses for autocomplete
   */
  async search(ctx: ServiceContext, query: string, limit = 10): Promise<Warehouse[]> {
    const queryBuilder = this.warehouseRepository
      .createQueryBuilder('warehouse')
      .where('warehouse.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('warehouse.deleted_at IS NULL')
      .andWhere('warehouse.is_active = true')
      .andWhere('(warehouse.code ILIKE :query OR warehouse.name ILIKE :query)', {
        query: `%${query}%`,
      });

    if (ctx.companyId) {
      queryBuilder.andWhere('warehouse.company_id = :companyId', { companyId: ctx.companyId });
    }

    return queryBuilder
      .orderBy('warehouse.name', 'ASC')
      .take(limit)
      .getMany();
  }

  /**
   * Get warehouses by branch
   */
  async findByBranch(ctx: ServiceContext, branchId: string): Promise<Warehouse[]> {
    return this.warehouseRepository.find({
      where: {
        tenantId: ctx.tenantId,
        branchId,
        isActive: true,
        deletedAt: IsNull(),
      },
      order: { name: 'ASC' },
    });
  }

  /**
   * Check if warehouse code is available
   */
  async isCodeAvailable(ctx: ServiceContext, code: string, excludeId?: string): Promise<boolean> {
    const queryBuilder = this.warehouseRepository
      .createQueryBuilder('warehouse')
      .where('warehouse.tenant_id = :tenantId', { tenantId: ctx.tenantId })
      .andWhere('warehouse.code = :code', { code });

    if (ctx.companyId) {
      queryBuilder.andWhere('warehouse.company_id = :companyId', { companyId: ctx.companyId });
    }

    if (excludeId) {
      queryBuilder.andWhere('warehouse.id != :excludeId', { excludeId });
    }

    const count = await queryBuilder.getCount();
    return count === 0;
  }
}
