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

import { CreateWagonTypeDto } from './dto/create-wagon-type.dto';
import { UpdateWagonTypeDto } from './dto/update-wagon-type.dto';
import { WagonType } from './entities/wagon-type.entity';
import { WagonTypesRepository } from './wagon-types.repository';

type WagonTypeListFilter = {
  isActive?: boolean;
  page?: number;
  pageSize?: number;
  sortBy?: string;
  sortOrder?: string;
};

@Injectable()
export class WagonTypesService {
  constructor(private readonly wagonTypesRepository: WagonTypesRepository) {}

  async findAll(filter: WagonTypeListFilter = {}): Promise<{
    data: WagonType[];
    meta: { total: number; page: number; pageSize: number; totalPages: number };
  }> {
    const page = filter.page ?? 1;
    const pageSize = filter.pageSize ?? 500;
    const sortBy = ['code', 'name', 'capacityTons', 'lengthMeters', 'isActive'].includes(
      filter.sortBy ?? '',
    )
      ? (filter.sortBy as keyof WagonType)
      : 'code';
    const sortOrder = filter.sortOrder?.toUpperCase() === 'DESC' ? 'DESC' : 'ASC';

    const [data, total] = await this.wagonTypesRepository.findAndCount({
      where: filter.isActive === undefined ? {} : { isActive: filter.isActive },
      order: { [sortBy]: sortOrder } as FindOptionsOrder<WagonType>,
      skip: (page - 1) * pageSize,
      take: pageSize,
    });

    return {
      data,
      meta: {
        total,
        page,
        pageSize,
        totalPages: Math.max(1, Math.ceil(total / pageSize)),
      },
    };
  }

  async findById(id: string): Promise<WagonType> {
    const wagonType = await this.wagonTypesRepository.findById(id);

    if (!wagonType) {
      throw new NotFoundException(`Wagon type ${id} not found`);
    }

    return wagonType;
  }

  async findByCode(code: string): Promise<WagonType> {
    const wagonType = await this.wagonTypesRepository.findByCode(code);
    if (!wagonType) {
      throw new NotFoundException(`Wagon type ${code} not found`);
    }
    return wagonType;
  }

  async create(dto: CreateWagonTypeDto): Promise<WagonType> {
    const code = dto.code.trim().toUpperCase();
    const existing = await this.wagonTypesRepository.findByCode(code);

    if (existing) {
      throw new ConflictException(`Wagon type code "${code}" already exists`);
    }

    return this.wagonTypesRepository.create({
      code,
      name: dto.name.trim(),
      capacityTons: dto.capacityTons,
      lengthMeters: dto.lengthMeters,
      maxWagonsPerTrain: dto.maxWagonsPerTrain ?? null,
      supportedLoadTypes: dto.supportedLoadTypes ?? [],
      isActive: dto.isActive ?? true,
    });
  }

  async update(id: string, dto: UpdateWagonTypeDto): Promise<WagonType> {
    const wagonType = await this.findById(id);
    const nextCode = dto.code?.trim().toUpperCase();

    if (nextCode && nextCode !== wagonType.code) {
      const existing = await this.wagonTypesRepository.findByCode(nextCode);
      if (existing) {
        throw new ConflictException(`Wagon type code "${nextCode}" already exists`);
      }
    }

    const updated = await this.wagonTypesRepository.update(id, {
      ...dto,
      ...(nextCode ? { code: nextCode } : {}),
      ...(dto.name ? { name: dto.name.trim() } : {}),
      maxWagonsPerTrain:
        dto.maxWagonsPerTrain === undefined ? undefined : dto.maxWagonsPerTrain ?? null,
      supportedLoadTypes: dto.supportedLoadTypes ?? undefined,
    });

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

    return updated;
  }

  async remove(id: string): Promise<void> {
    await this.findById(id);
    await this.wagonTypesRepository.softDelete(id);
  }
}
