import { Repository, FindOptionsWhere, ILike } from 'typeorm';
import { Lane } from '../entities';

export interface LaneSearchParams {
  tenantId: string;
  search?: string;
  origenCiudad?: string;
  origenEstado?: string;
  destinoCiudad?: string;
  destinoEstado?: string;
  activo?: boolean;
  limit?: number;
  offset?: number;
}

export interface CreateLaneDto {
  codigo: string;
  nombre: string;
  origenCiudad: string;
  origenEstado: string;
  origenCodigoPostal?: string;
  destinoCiudad: string;
  destinoEstado: string;
  destinoCodigoPostal?: string;
  distanciaKm?: number;
  tiempoEstimadoHoras?: number;
}

export interface UpdateLaneDto extends Partial<CreateLaneDto> {
  activo?: boolean;
}

export class LanesService {
  constructor(private readonly laneRepository: Repository<Lane>) {}

  async findAll(params: LaneSearchParams): Promise<{ data: Lane[]; total: number }> {
    const {
      tenantId,
      search,
      origenCiudad,
      origenEstado,
      destinoCiudad,
      destinoEstado,
      activo,
      limit = 50,
      offset = 0,
    } = params;

    const where: FindOptionsWhere<Lane>[] = [];
    const baseWhere: FindOptionsWhere<Lane> = { tenantId };

    if (activo !== undefined) {
      baseWhere.activo = activo;
    }

    if (origenCiudad) {
      baseWhere.origenCiudad = ILike(`%${origenCiudad}%`);
    }

    if (origenEstado) {
      baseWhere.origenEstado = ILike(`%${origenEstado}%`);
    }

    if (destinoCiudad) {
      baseWhere.destinoCiudad = ILike(`%${destinoCiudad}%`);
    }

    if (destinoEstado) {
      baseWhere.destinoEstado = ILike(`%${destinoEstado}%`);
    }

    if (search) {
      where.push(
        { ...baseWhere, codigo: ILike(`%${search}%`) },
        { ...baseWhere, nombre: ILike(`%${search}%`) },
        { ...baseWhere, origenCiudad: ILike(`%${search}%`) },
        { ...baseWhere, destinoCiudad: ILike(`%${search}%`) }
      );
    } else {
      where.push(baseWhere);
    }

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

    return { data, total };
  }

  async findOne(id: string, tenantId: string): Promise<Lane | null> {
    return this.laneRepository.findOne({
      where: { id, tenantId },
    });
  }

  async findByCodigo(codigo: string, tenantId: string): Promise<Lane | null> {
    return this.laneRepository.findOne({
      where: { codigo, tenantId },
    });
  }

  async findByOrigenDestino(
    origenCiudad: string,
    destinoCiudad: string,
    tenantId: string
  ): Promise<Lane | null> {
    return this.laneRepository.findOne({
      where: {
        origenCiudad: ILike(origenCiudad),
        destinoCiudad: ILike(destinoCiudad),
        tenantId,
        activo: true,
      },
    });
  }

  async create(tenantId: string, dto: CreateLaneDto, createdBy: string): Promise<Lane> {
    // Check if codigo already exists
    const existingCodigo = await this.findByCodigo(dto.codigo, tenantId);
    if (existingCodigo) {
      throw new Error(`Ya existe una lane con el codigo "${dto.codigo}"`);
    }

    const lane = this.laneRepository.create({
      ...dto,
      tenantId,
      activo: true,
      createdById: createdBy,
    });

    return this.laneRepository.save(lane);
  }

  async update(
    id: string,
    tenantId: string,
    dto: UpdateLaneDto,
    updatedBy: string
  ): Promise<Lane | null> {
    const lane = await this.findOne(id, tenantId);
    if (!lane) return null;

    // Check for codigo uniqueness if changing codigo
    if (dto.codigo && dto.codigo !== lane.codigo) {
      const existingCodigo = await this.findByCodigo(dto.codigo, tenantId);
      if (existingCodigo && existingCodigo.id !== id) {
        throw new Error(`Ya existe una lane con el codigo "${dto.codigo}"`);
      }
    }

    Object.assign(lane, dto);
    return this.laneRepository.save(lane);
  }

  async activar(id: string, tenantId: string): Promise<Lane | null> {
    const lane = await this.findOne(id, tenantId);
    if (!lane) return null;

    lane.activo = true;
    return this.laneRepository.save(lane);
  }

  async desactivar(id: string, tenantId: string): Promise<Lane | null> {
    const lane = await this.findOne(id, tenantId);
    if (!lane) return null;

    lane.activo = false;
    return this.laneRepository.save(lane);
  }

  // Get lanes by origin state
  async getLanesByOrigenEstado(origenEstado: string, tenantId: string): Promise<Lane[]> {
    return this.laneRepository.find({
      where: { origenEstado: ILike(`%${origenEstado}%`), tenantId, activo: true },
      order: { origenCiudad: 'ASC', destinoCiudad: 'ASC' },
    });
  }

  // Get lanes by destination state
  async getLanesByDestinoEstado(destinoEstado: string, tenantId: string): Promise<Lane[]> {
    return this.laneRepository.find({
      where: { destinoEstado: ILike(`%${destinoEstado}%`), tenantId, activo: true },
      order: { origenCiudad: 'ASC', destinoCiudad: 'ASC' },
    });
  }

  // Get all unique origin cities
  async getOrigenCiudades(tenantId: string): Promise<string[]> {
    const lanes = await this.laneRepository
      .createQueryBuilder('l')
      .select('DISTINCT l.origen_ciudad', 'ciudad')
      .where('l.tenant_id = :tenantId', { tenantId })
      .andWhere('l.activo = true')
      .orderBy('l.origen_ciudad', 'ASC')
      .getRawMany();

    return lanes.map(l => l.ciudad);
  }

  // Get all unique destination cities
  async getDestinoCiudades(tenantId: string): Promise<string[]> {
    const lanes = await this.laneRepository
      .createQueryBuilder('l')
      .select('DISTINCT l.destino_ciudad', 'ciudad')
      .where('l.tenant_id = :tenantId', { tenantId })
      .andWhere('l.activo = true')
      .orderBy('l.destino_ciudad', 'ASC')
      .getRawMany();

    return lanes.map(l => l.ciudad);
  }

  // Get all unique origin states
  async getOrigenEstados(tenantId: string): Promise<string[]> {
    const lanes = await this.laneRepository
      .createQueryBuilder('l')
      .select('DISTINCT l.origen_estado', 'estado')
      .where('l.tenant_id = :tenantId', { tenantId })
      .andWhere('l.activo = true')
      .orderBy('l.origen_estado', 'ASC')
      .getRawMany();

    return lanes.map(l => l.estado);
  }

  // Get all unique destination states
  async getDestinoEstados(tenantId: string): Promise<string[]> {
    const lanes = await this.laneRepository
      .createQueryBuilder('l')
      .select('DISTINCT l.destino_estado', 'estado')
      .where('l.tenant_id = :tenantId', { tenantId })
      .andWhere('l.activo = true')
      .orderBy('l.destino_estado', 'ASC')
      .getRawMany();

    return lanes.map(l => l.estado);
  }

  // Search for lane or create if not exists
  async findOrCreate(
    tenantId: string,
    dto: CreateLaneDto,
    createdBy: string
  ): Promise<{ lane: Lane; created: boolean }> {
    // Try to find by origen-destino first
    let lane = await this.findByOrigenDestino(dto.origenCiudad, dto.destinoCiudad, tenantId);

    if (lane) {
      return { lane, created: false };
    }

    // Try to find by codigo
    lane = await this.findByCodigo(dto.codigo, tenantId);
    if (lane) {
      return { lane, created: false };
    }

    // Create new lane
    const newLane = await this.create(tenantId, dto, createdBy);
    return { lane: newLane, created: true };
  }

  // Calculate reverse lane (destino -> origen)
  async getLaneInversa(id: string, tenantId: string): Promise<Lane | null> {
    const lane = await this.findOne(id, tenantId);
    if (!lane) return null;

    return this.findByOrigenDestino(lane.destinoCiudad, lane.origenCiudad, tenantId);
  }

  // Create reverse lane automatically
  async crearLaneInversa(id: string, tenantId: string, createdBy: string): Promise<Lane | null> {
    const lane = await this.findOne(id, tenantId);
    if (!lane) return null;

    // Check if reverse already exists
    const existing = await this.findByOrigenDestino(
      lane.destinoCiudad,
      lane.origenCiudad,
      tenantId
    );
    if (existing) {
      return existing;
    }

    // Generate reverse codigo
    const reverseCodigo = `${lane.codigo}-REV`;
    const reverseNombre = `${lane.destinoCiudad} - ${lane.origenCiudad}`;

    return this.create(
      tenantId,
      {
        codigo: reverseCodigo,
        nombre: reverseNombre,
        origenCiudad: lane.destinoCiudad,
        origenEstado: lane.destinoEstado,
        origenCodigoPostal: lane.destinoCodigoPostal ?? undefined,
        destinoCiudad: lane.origenCiudad,
        destinoEstado: lane.origenEstado,
        destinoCodigoPostal: lane.origenCodigoPostal ?? undefined,
        distanciaKm: lane.distanciaKm ?? undefined,
        tiempoEstimadoHoras: lane.tiempoEstimadoHoras ?? undefined,
      },
      createdBy
    );
  }

  async delete(id: string, tenantId: string): Promise<boolean> {
    const lane = await this.findOne(id, tenantId);
    if (!lane) return false;

    // Note: Should check if lane is used in any tarifas before deleting
    const result = await this.laneRepository.delete(id);
    return (result.affected ?? 0) > 0;
  }
}
