import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';

import { CreateLocomotiveDto } from './dto/create-locomotive.dto';
import { FilterLocomotivesDto } from './dto/filter-locomotives.dto';
import { UpdateLocomotiveDto } from './dto/update-locomotive.dto';

import {
  Locomotive,
  type LocomotiveStatus,
  type LocomotiveType,
} from './entities/locomotive.entity';
import { LocomotivesRepository } from './locomotives.repository';

@Injectable()
export class LocomotivesService {
  constructor(private readonly locomotivesRepository: LocomotivesRepository) {}

  findAll(filter: FilterLocomotivesDto): Promise<Locomotive[]> {
    return this.locomotivesRepository.findAll({
      where: {
        ...(filter.status ? { status: filter.status as LocomotiveStatus } : {}),
        ...(filter.locomotiveType
          ? { locomotiveType: filter.locomotiveType as LocomotiveType }
          : {}),
        ...(filter.currentYardId ? { currentYardId: filter.currentYardId } : {}),
      },
      relations: { currentYard: true },
      order: { code: 'ASC' },
    });
  }

  async create(dto: CreateLocomotiveDto): Promise<Locomotive> {
    const [existing] = await this.locomotivesRepository.findAll({ where: { code: dto.code } });

    if (existing) {
      throw new ConflictException(`Locomotive code ${dto.code} already exists`);
    }

    return this.locomotivesRepository.create({
      code: dto.code,
      name: dto.name?.trim() || null,
      locomotiveType: dto.locomotiveType as LocomotiveType,
      status: dto.status as LocomotiveStatus,
      currentYardId: dto.currentYardId ?? null,
      maxPullWeightTons: dto.maxPullWeightTons,
      maxTrainLengthMeters: dto.maxTrainLengthMeters,
      powerKw: dto.powerKw ?? null,
      tractionForceKn: dto.tractionForceKn ?? null,
      maxSpeedKmh: dto.maxSpeedKmh ?? null,
    });
  }

  async findById(id: string): Promise<Locomotive> {
    const locomotive = await this.locomotivesRepository.findById(id, {
      relations: { currentYard: true },
    });

    if (!locomotive) {
      throw new NotFoundException(`Locomotive ${id} not found`);
    }

    return locomotive;
  }

  async update(id: string, dto: UpdateLocomotiveDto): Promise<Locomotive> {
    const locomotive = await this.findById(id);

    if (dto.code && dto.code !== locomotive.code) {
      const [existing] = await this.locomotivesRepository.findAll({ where: { code: dto.code } });
      if (existing && existing.id !== id) {
        throw new ConflictException(`Locomotive code ${dto.code} already exists`);
      }
    }

    const updated = await this.locomotivesRepository.update(id, {
      ...dto,
      locomotiveType:
        dto.locomotiveType === undefined ? locomotive.locomotiveType : dto.locomotiveType as LocomotiveType,
      status: dto.status === undefined ? locomotive.status : dto.status as LocomotiveStatus,
      currentYardId:
        dto.currentYardId === undefined
          ? locomotive.currentYardId
          : (dto.currentYardId ?? null),
      name: dto.name === undefined ? locomotive.name : dto.name?.trim() || null,
      powerKw: dto.powerKw === undefined ? locomotive.powerKw : dto.powerKw ?? null,
      tractionForceKn:
        dto.tractionForceKn === undefined ? locomotive.tractionForceKn : dto.tractionForceKn ?? null,
      maxSpeedKmh:
        dto.maxSpeedKmh === undefined ? locomotive.maxSpeedKmh : dto.maxSpeedKmh ?? null,
    });

    if (!updated) {
      throw new NotFoundException(`Locomotive ${id} not found`);
    }

    return updated;
  }

  async decommission(id: string): Promise<Locomotive> {
    await this.findById(id);

    const updated = await this.locomotivesRepository.update(id, {
      status: 'OUT_OF_SERVICE',
    });

    if (!updated) {
      throw new NotFoundException(`Locomotive ${id} not found`);
    }

    return updated;
  }
}
