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

import { CreateTruckTypeDto } from './dto/create-truck-type.dto';
import { UpdateTruckTypeDto } from './dto/update-truck-type.dto';
import { TruckType } from './entities/truck-type.entity';
import { TruckTypesRepository } from './truck-types.repository';

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

@Injectable()
export class TruckTypesService {
  constructor(private readonly truckTypesRepository: TruckTypesRepository) {}

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

    const [data, total] = await this.truckTypesRepository.findAndCount({
      where: filter.isActive === undefined ? {} : { isActive: filter.isActive },
      order: { [sortBy]: sortOrder } as FindOptionsOrder<TruckType>,
      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<TruckType> {
    const truckType = await this.truckTypesRepository.findById(id);

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

    return truckType;
  }

  async findByCode(code: string): Promise<TruckType> {
    const truckType = await this.truckTypesRepository.findByCode(code);
    if (!truckType) {
      throw new NotFoundException(`Truck type ${code} not found`);
    }
    return truckType;
  }

  async create(dto: CreateTruckTypeDto): Promise<TruckType> {
    const code = dto.code.trim().toUpperCase();
    const existing = await this.truckTypesRepository.findByCode(code);

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

    return this.truckTypesRepository.create({
      code,
      name: dto.name.trim(),
      capacityTons: dto.capacityTons ?? null,
      hasTrailer: dto.hasTrailer ?? false,
      description: dto.description?.trim() ?? null,
      isActive: dto.isActive ?? true,
    });
  }

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

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

    const updated = await this.truckTypesRepository.update(id, {
      ...dto,
      ...(nextCode ? { code: nextCode } : {}),
      ...(dto.name ? { name: dto.name.trim() } : {}),
    });

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

    return updated;
  }

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