import { Injectable, NotFoundException, ConflictException } from '@nestjs/common';
import { EntityManager, RequiredEntityData } from '@mikro-orm/postgresql';
import { Delivery } from '../entities/delivery.entity';
import { DeliveryRepository } from '../repositories/delivery.repository';
import { CreateDeliveryDto } from '../dto/create-delivery.dto';
import { UpdateDeliveryDto } from '../dto/update-delivery.dto';
import { DeliveryMethodEnum } from '../interface/delivery';
import { DeliveryMessage } from 'src/common/enums/message.enum';
import { RestaurantsService } from 'src/modules/restaurants/providers/restaurants.service';

@Injectable()
export class DeliveryService {
  constructor(
    private readonly deliveryRepository: DeliveryRepository,
    private readonly restService: RestaurantsService,
    private readonly em: EntityManager,
  ) { }

  async create(restId: string, createDto: CreateDeliveryDto): Promise<Delivery> {
    const restaurant = await this.restService.findOneOrFail(restId)

    // Check if the delivery method with the same name already exists for this restaurant
    const existing = await this.deliveryRepository.findOne({
      restaurant: { id: restId },
      method: createDto.method,
    });

    if (existing) {
      throw new ConflictException('این روش ارسال قبلاً برای این رستوران ثبت شده است.');
    }

    const data: RequiredEntityData<Delivery> = {
      restaurant,
      method: createDto.method,
      deliveryFee: createDto.deliveryFee,
      minOrderPrice: createDto.minOrderPrice,
      description: createDto.description,
      enabled: createDto.enabled ?? true,
      order: createDto.order ?? 0,
      deliveryFeeType: createDto.deliveryFeeType,
      perKilometerFee: createDto.perKilometerFee ?? null,
      distanceBasedMinCost: createDto.distanceBasedMinCost ?? null,
    };

    const delivery = this.deliveryRepository.create(data);
    await this.em.persistAndFlush(delivery);
    return delivery;
  }

  async findByRestaurantId(restId: string): Promise<Delivery[]> {
    return this.deliveryRepository.find({ restaurant: { id: restId } }, { orderBy: { order: 'asc' } });
  }

  async findOneOrFail(restId: string, deliveryId: string): Promise<Delivery> {
    const delivery = await this.deliveryRepository.findOne({
      id: deliveryId,
      restaurant: { id: restId },
    });

    if (!delivery) {
      throw new NotFoundException(DeliveryMessage.DELIVERY_METHOD_NOT_FOUND);
    }

    return delivery;
  }

  async update(restId: string, deliveryId: string, updateDto: UpdateDeliveryDto): Promise<Delivery> {
    const delivery = await this.findOneOrFail(restId, deliveryId)
    // If method is being updated, check for conflicts
    if (updateDto.method !== undefined && updateDto.method !== delivery.method) {
      const existing = await this.deliveryRepository.findOne({
        restaurant: { id: restId },
        method: updateDto.method,
        id: { $ne: deliveryId },
      });

      if (existing) {
        throw new ConflictException('این روش ارسال قبلاً برای این رستوران ثبت شده است.');
      }
    }

    this.em.assign(delivery, updateDto);
    await this.em.persistAndFlush(delivery);

    return delivery;
  }

  async remove(restId: string, deliveryId: string): Promise<void> {
    const delivery = await this.findOneOrFail(restId, deliveryId)

    await this.em.removeAndFlush(delivery);
  }

  findAll(): DeliveryMethodEnum[] {
    return Object.values(DeliveryMethodEnum);
  }
}
