import { FindOptionsWhere, ILike, IsNull } from 'typeorm';
import { AppDataSource } from '../../config/typeorm.js';
import { Warehouse } from './entities/warehouse.entity.js';
import { WarehouseLocation } from './entities/warehouse-location.entity.js';

export interface WarehouseSearchParams {
  tenantId: string;
  search?: string;
  isActive?: boolean;
  limit?: number;
  offset?: number;
}

export interface LocationSearchParams {
  warehouseId?: string;
  parentId?: string;
  locationType?: 'zone' | 'aisle' | 'rack' | 'shelf' | 'bin';
  isActive?: boolean;
  limit?: number;
  offset?: number;
}

export interface CreateWarehouseDto {
  code: string;
  name: string;
  address?: string;
  city?: string;
  state?: string;
  country?: string;
  postalCode?: string;
  phone?: string;
  email?: string;
  isActive?: boolean;
  isDefault?: boolean;
}

export interface UpdateWarehouseDto {
  code?: string;
  name?: string;
  address?: string | null;
  city?: string | null;
  state?: string | null;
  country?: string | null;
  postalCode?: string | null;
  phone?: string | null;
  email?: string | null;
  isActive?: boolean;
  isDefault?: boolean;
}

export interface CreateLocationDto {
  warehouseId: string;
  code: string;
  name: string;
  parentId?: string;
  locationType?: 'zone' | 'aisle' | 'rack' | 'shelf' | 'bin';
  barcode?: string;
  isActive?: boolean;
}

export interface UpdateLocationDto {
  code?: string;
  name?: string;
  parentId?: string | null;
  locationType?: 'zone' | 'aisle' | 'rack' | 'shelf' | 'bin';
  barcode?: string | null;
  isActive?: boolean;
}

class WarehousesServiceClass {
  private get warehouseRepository() {
    return AppDataSource.getRepository(Warehouse);
  }

  private get locationRepository() {
    return AppDataSource.getRepository(WarehouseLocation);
  }

  // ==================== Warehouses ====================

  async findAll(params: WarehouseSearchParams): Promise<{ data: Warehouse[]; total: number }> {
    const { tenantId, search, isActive, limit = 50, offset = 0 } = params;

    const where: FindOptionsWhere<Warehouse> = { tenantId, deletedAt: IsNull() };

    if (isActive !== undefined) {
      where.isActive = isActive;
    }

    if (search) {
      const [data, total] = await this.warehouseRepository.findAndCount({
        where: [
          { ...where, name: ILike(`%${search}%`) },
          { ...where, code: ILike(`%${search}%`) },
        ],
        take: limit,
        skip: offset,
        order: { name: 'ASC' },
      });
      return { data, total };
    }

    const [data, total] = await this.warehouseRepository.findAndCount({
      where,
      take: limit,
      skip: offset,
      order: { isDefault: 'DESC', name: 'ASC' },
    });

    return { data, total };
  }

  async findOne(id: string, tenantId: string): Promise<Warehouse | null> {
    return this.warehouseRepository.findOne({
      where: { id, tenantId, deletedAt: IsNull() },
    });
  }

  async findByCode(code: string, tenantId: string): Promise<Warehouse | null> {
    return this.warehouseRepository.findOne({
      where: { code, tenantId, deletedAt: IsNull() },
    });
  }

  async getDefault(tenantId: string): Promise<Warehouse | null> {
    return this.warehouseRepository.findOne({
      where: { tenantId, isDefault: true, deletedAt: IsNull() },
    });
  }

  async getActive(tenantId: string): Promise<Warehouse[]> {
    return this.warehouseRepository.find({
      where: { tenantId, isActive: true, deletedAt: IsNull() },
      order: { isDefault: 'DESC', name: 'ASC' },
    });
  }

  async create(tenantId: string, dto: CreateWarehouseDto, _createdBy?: string): Promise<Warehouse> {
    // Validate unique code within tenant (RLS compliance)
    const existingCode = await this.warehouseRepository.findOne({
      where: { code: dto.code, tenantId, deletedAt: IsNull() },
    });
    if (existingCode) {
      throw new Error(`Warehouse with code '${dto.code}' already exists`);
    }

    // If this is set as default, unset other defaults
    if (dto.isDefault) {
      await this.warehouseRepository.update(
        { tenantId, isDefault: true },
        { isDefault: false }
      );
    }

    const warehouse = this.warehouseRepository.create({
      ...dto,
      tenantId,
    });
    return this.warehouseRepository.save(warehouse);
  }

  async update(id: string, tenantId: string, dto: UpdateWarehouseDto, _updatedBy?: string): Promise<Warehouse | null> {
    const warehouse = await this.findOne(id, tenantId);
    if (!warehouse) return null;

    // If setting as default, unset other defaults
    if (dto.isDefault && !warehouse.isDefault) {
      await this.warehouseRepository.update(
        { tenantId, isDefault: true },
        { isDefault: false }
      );
    }

    Object.assign(warehouse, dto);
    return this.warehouseRepository.save(warehouse);
  }

  async delete(id: string, tenantId: string): Promise<boolean> {
    const result = await this.warehouseRepository.softDelete({ id, tenantId });
    return (result.affected ?? 0) > 0;
  }

  // ==================== Locations ====================
  // Note: Locations don't have tenantId directly, they belong to a Warehouse which has tenantId

  async findAllLocations(params: LocationSearchParams & { tenantId: string }): Promise<{ data: WarehouseLocation[]; total: number }> {
    const { tenantId, warehouseId, parentId, locationType, isActive, limit = 50, offset = 0 } = params;

    // Build query to join with warehouse for tenant filtering
    const queryBuilder = this.locationRepository
      .createQueryBuilder('location')
      .leftJoinAndSelect('location.warehouse', 'warehouse')
      .where('warehouse.tenantId = :tenantId', { tenantId })
      .andWhere('location.deletedAt IS NULL');

    if (warehouseId) {
      queryBuilder.andWhere('location.warehouseId = :warehouseId', { warehouseId });
    }

    if (parentId) {
      queryBuilder.andWhere('location.parentId = :parentId', { parentId });
    }

    if (locationType) {
      queryBuilder.andWhere('location.locationType = :locationType', { locationType });
    }

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

    queryBuilder.orderBy('location.code', 'ASC');
    queryBuilder.skip(offset).take(limit);

    const [data, total] = await queryBuilder.getManyAndCount();
    return { data, total };
  }

  async findLocation(id: string, tenantId: string): Promise<WarehouseLocation | null> {
    return this.locationRepository
      .createQueryBuilder('location')
      .leftJoinAndSelect('location.warehouse', 'warehouse')
      .where('location.id = :id', { id })
      .andWhere('warehouse.tenantId = :tenantId', { tenantId })
      .andWhere('location.deletedAt IS NULL')
      .getOne();
  }

  async createLocation(_tenantId: string, dto: CreateLocationDto, _createdBy?: string): Promise<WarehouseLocation> {
    const location = this.locationRepository.create({
      warehouseId: dto.warehouseId,
      code: dto.code,
      name: dto.name,
      parentId: dto.parentId,
      locationType: dto.locationType || 'shelf',
      barcode: dto.barcode,
      isActive: dto.isActive ?? true,
    });
    return this.locationRepository.save(location);
  }

  async updateLocation(id: string, tenantId: string, dto: UpdateLocationDto, _updatedBy?: string): Promise<WarehouseLocation | null> {
    const location = await this.findLocation(id, tenantId);
    if (!location) return null;
    Object.assign(location, dto);
    return this.locationRepository.save(location);
  }

  async deleteLocation(id: string, tenantId: string): Promise<boolean> {
    const location = await this.findLocation(id, tenantId);
    if (!location) return false;
    const result = await this.locationRepository.softDelete({ id });
    return (result.affected ?? 0) > 0;
  }

  async getLocationsByWarehouse(warehouseId: string, tenantId: string): Promise<WarehouseLocation[]> {
    return this.locationRepository
      .createQueryBuilder('location')
      .leftJoin('location.warehouse', 'warehouse')
      .where('location.warehouseId = :warehouseId', { warehouseId })
      .andWhere('warehouse.tenantId = :tenantId', { tenantId })
      .andWhere('location.isActive = :isActive', { isActive: true })
      .andWhere('location.deletedAt IS NULL')
      .orderBy('location.code', 'ASC')
      .getMany();
  }
}

// Export singleton instance
export const warehousesService = new WarehousesServiceClass();
