import { Injectable, NotFoundException, Inject, Optional, BadRequestException } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { PrismaService } from '../../common/prisma.service';
import { AuditService } from '../../common/audit.service';
import { CreateStationDto } from './stations.dto';
import { DeleteOperationException } from '../../common/exceptions/delete-operation.exception';

interface StationFilters {
  search?: string;
  country?: string;
  operational?: string;
}

@Injectable()
export class StationsService {
  constructor(
    private prisma: PrismaService,
    private auditService: AuditService,
    @Optional() @Inject(REQUEST) private request?: any,
  ) {}
  
  findAll(filters: StationFilters = {}) {
    const where: any = {};
    
    if (filters.search) {
      where.OR = [
        { name: { contains: filters.search, mode: 'insensitive' } },
        { code: { contains: filters.search, mode: 'insensitive' } },
        { city: { contains: filters.search, mode: 'insensitive' } },
      ];
    }
    
    if (filters.country) {
      where.countryCode = filters.country;
    }
    
    // Default to operational stations only; allow explicit override (e.g. back-office)
    if (filters.operational !== undefined && filters.operational !== '') {
      where.isOperational = filters.operational === 'true';
    } else {
      where.isOperational = true;
    }
    
    return this.prisma.station.findMany({ 
      where,
      orderBy: { sequence: 'asc' } 
    }); 
  }
  
  async findOne(id: string) {
    const s = await this.prisma.station.findUnique({ where: { id } });
    if (!s) throw new NotFoundException('Station not found');
    if (!s.isOperational) throw new NotFoundException('Station is not operational');
    return s;
  }
  
  async create(dto: CreateStationDto) {
    const { lat, lng, ...rest } = dto;
    const station = await this.prisma.station.create({
      data: { ...rest, ...(lat !== undefined && { lat }), ...(lng !== undefined && { lng }) } as any,
    });
    
    await this.auditService.log({
      userId: this.request?.user?.id,
      action: 'CREATE',
      entityType: 'Station',
      entityId: station.id,
      newData: station,
    });

    return station;
  }

  async update(id: string, dto: Partial<CreateStationDto>) {
    const oldStation = await this.findOne(id);
    const { code, name, city, lat, lng } = dto;
    const data: any = { code, name, city, lat, lng };
    if ('countryCode' in dto) data.countryCode = (dto as any).countryCode;
    if ('sequence' in dto) data.sequence = (dto as any).sequence;
    if ('isOperational' in dto) data.isOperational = (dto as any).isOperational;
    
    const updatedStation = await this.prisma.station.update({
      where: { id },
      data,
    });

    await this.auditService.log({
      userId: this.request?.user?.id,
      action: 'UPDATE',
      entityType: 'Station',
      entityId: id,
      oldData: oldStation,
      newData: updatedStation,
    });

    return updatedStation;
  }

  async remove(id: string, cascade = false) {
    const station = await this.prisma.station.findUnique({
      where: { id },
      include: {
        _count: { select: { stopTimes: true } },
        originSchedules: { take: 1, select: { id: true } },
        destinationSchedules: { take: 1, select: { id: true } },
      },
    });
    if (!station) throw new NotFoundException('Station not found');

    if (!cascade) {
      const [routeStopCount, originCount, destCount, stopTimeCount] = await Promise.all([
        this.prisma.routeStop.count({ where: { stationId: id } }),
        this.prisma.trainSchedule.count({ where: { originStationId: id } }),
        this.prisma.trainSchedule.count({ where: { destinationStationId: id } }),
        (station as any)._count.stopTimes as number,
      ]);

      const constraints = [];
      if (routeStopCount > 0)
        constraints.push({ entityName: 'route', count: routeStopCount, action: 'delete' as const });
      const scheduleCount = originCount + destCount;
      if (scheduleCount > 0)
        constraints.push({ entityName: 'schedule', count: scheduleCount, action: 'delete' as const });
      if (stopTimeCount > 0)
        constraints.push({ entityName: 'stop time', count: stopTimeCount, action: 'delete' as const });

      if (constraints.length > 0)
        throw new DeleteOperationException('Station', `${station.name} (${station.code})`, constraints);
    }

    if (cascade) {
      await this.prisma.tripStopTime.deleteMany({ where: { stationId: id } });
      await this.prisma.trainSchedule.deleteMany({
        where: { OR: [{ originStationId: id }, { destinationStationId: id }] },
      });
      await this.prisma.routeStop.deleteMany({ where: { stationId: id } });
    }

    const deleted = await this.prisma.station.delete({ where: { id } });

    await this.auditService.log({
      userId: this.request?.user?.id,
      action: 'DELETE',
      entityType: 'Station',
      entityId: id,
      oldData: station,
    });

    return deleted;
  }
}
