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

import { BookingsRepository } from '../bookings/bookings.repository';
import { CreateFirstMileDto } from './dto/create-first-mile.dto';
import { UpdateFirstMileDto } from './dto/update-first-mile.dto';
import { FirstMile, FirstMileStatus } from './entities/first-mile.entity';
import { FirstMileRepository } from './first-mile.repository';

type FirstMileListFilter = {
  status?: FirstMileStatus;
  bookingId?: string;
  vehicleId?: string;
  page?: number;
  pageSize?: number;
  sortBy?: string;
  sortOrder?: string;
};

const SORTABLE_FIELDS: (keyof FirstMile)[] = [
  'status',
  'advancedPayment',
  'remainingPayment',
  'createdAt',
];

@Injectable()
export class FirstMileService {
  constructor(
    private readonly firstMileRepository: FirstMileRepository,
    private readonly bookingsRepository: BookingsRepository,
  ) {}

  /**
   * Look up a booking by its human-readable reference and confirm it has been
   * paid before any first-mile work proceeds. Throws if the reference is
   * unknown or the booking has not reached PAID status.
   */
  async acceptBooking(bookingReference: string): Promise<FirstMile | null> {
    const booking = await this.bookingsRepository.findByReference(bookingReference);

    if (!booking) {
      return null;
    }

    if (booking.paymentStatus !== 'PAID') {
      return null;
    }

    return this.create({
      bookingId: booking.id,
      advancedPayment: 0,
    });
  }

  async findAll(filter: FirstMileListFilter = {}): Promise<{
    data: FirstMile[];
    meta: { total: number; page: number; pageSize: number; totalPages: number };
  }> {
    const page = filter.page ?? 1;
    const pageSize = filter.pageSize ?? 50;
    const sortBy = SORTABLE_FIELDS.includes(filter.sortBy as keyof FirstMile)
      ? (filter.sortBy as keyof FirstMile)
      : 'createdAt';
    const sortOrder = filter.sortOrder?.toUpperCase() === 'ASC' ? 'ASC' : 'DESC';

    const where: FindOptionsWhere<FirstMile> = {};
    if (filter.status) where.status = filter.status;
    if (filter.bookingId) where.bookingId = filter.bookingId;
    if (filter.vehicleId) where.vehicleId = filter.vehicleId;

    const [data, total] = await this.firstMileRepository.findAndCount({
      where,
      relations: {
        booking: { company: true, serviceType: true, originYard: true, destinationYard: true, cargoType: true },
        vehicle: true,
      },
      order: { [sortBy]: sortOrder },
      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<FirstMile> {
    const record = await this.firstMileRepository.findById(id, {
      relations: {
        booking: { company: true, serviceType: true, originYard: true, destinationYard: true, cargoType: true },
        vehicle: true,
      },
    });

    if (!record) {
      throw new NotFoundException(`First-mile record ${id} not found`);
    }

    return record;
  }

  async create(dto: CreateFirstMileDto): Promise<FirstMile> {
    return this.firstMileRepository.create({
      bookingId: dto.bookingId,
      status: dto.status ?? 'PAYMENT_PENDING',
      advancedPayment: dto.advancedPayment ?? 0,
      remainingPayment: dto.remainingPayment ?? 0,
      estimatedKm: dto.estimatedKm ?? null,
      exactKm: dto.exactKm ?? null,
      vehicleId: dto.vehicleId ?? null,
    });
  }

  async update(id: string, dto: UpdateFirstMileDto): Promise<FirstMile> {
    await this.findById(id);

    const updated = await this.firstMileRepository.update(id, {
      ...(dto.bookingId !== undefined ? { bookingId: dto.bookingId } : {}),
      ...(dto.status !== undefined ? { status: dto.status } : {}),
      ...(dto.advancedPayment !== undefined ? { advancedPayment: dto.advancedPayment } : {}),
      ...(dto.remainingPayment !== undefined ? { remainingPayment: dto.remainingPayment } : {}),
      ...(dto.estimatedKm !== undefined ? { estimatedKm: dto.estimatedKm } : {}),
      ...(dto.exactKm !== undefined ? { exactKm: dto.exactKm } : {}),
      ...(dto.vehicleId !== undefined ? { vehicleId: dto.vehicleId } : {}),
    });

    if (!updated) {
      throw new NotFoundException(`First-mile record ${id} not found`);
    }

    return updated;
  }

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