import pumpReadingModel from "./pumpReading.model";
import { IPumpReading } from "./pumpReading.interface";
import fuelRequestModel from "../fuel-requests/fuelRequest.model";
import anomalyModel from "../anomalies/anomaly.model";
import { generatePumpReadingNumber } from "../../utils/requestNumber";


export class PumpReadingService{


    //Obtener todas las lecturas 

    async getAll(){

        return await pumpReadingModel
        .find()
        .populate("fuelPumpId")
        .populate("fuelTypeId")
        .populate("createdBy")
        .sort({
            readingDate:-1
        });
    }

    async getById(
        id:string
    ){

        return await pumpReadingModel
        .findById(id)
        .populate("fuelPumpId")
        .populate("fuelTypeId")
        .populate("createdBy")
    }

    //Crear nueva lectura de bomba
    async create(
    data: IPumpReading
    ){
        // Validar lectura inicial
        if (data.initialReading < 0) {
            throw new Error("La lectura inicial no puede ser menor que 0.");
        }

        // Validar lectura final
        if (data.finalReading < data.initialReading) {
            throw new Error("La lectura final no puede ser menor que la lectura inicial.");
        }

        data.readingNumber = generatePumpReadingNumber();

        // Calcular galones despachados
        data.gallonsDispensed = data.finalReading - data.initialReading;

        // Inicializar valores
        data.systemDispensed = 0;

        data.difference = 0;

        data.status = "ABIERTA";

        // Guardar lectura
        const pumpReading = new pumpReadingModel(data);

        return await pumpReading.save();
    }

    async update(
        id: string,
        data: Partial<IPumpReading>
    ){

        // Buscar la lectura actual
        const reading = await pumpReadingModel.findById(id);

        if (!reading) {
            return null;
        }

        // Tomar los nuevos valores o conservar los existentes
        const initialReading = data.initialReading ?? reading.initialReading;

        const finalReading = data.finalReading ?? reading.finalReading;

        // Validar lecturas
        if (initialReading < 0) {
            throw new Error("La lectura inicial no puede ser menor que 0.");
        }

        if (finalReading < initialReading) {
            throw new Error("La lectura final no puede ser menor que la lectura inicial.");
        }

        // Recalcular galones despachados
        data.gallonsDispensed = finalReading - initialReading;

        // Mantener actualizado el total del sistema
        const systemDispensed = data.systemDispensed ?? reading.systemDispensed;

        // Recalcular diferencia
        data.difference = data.gallonsDispensed - systemDispensed;

        // Actualizar estado automáticamente
        if (data.difference === 0) {
            data.status = "CUADRADA";
        } else {
            data.status = "CON_DIFERENCIA";
        }

        // Actualizar la lectura
        return await pumpReadingModel.findByIdAndUpdate(
            id,
            data,
            {
                new: true
            }
        );
    }

    async delete (
        id:string
    ){
        return await pumpReadingModel.findByIdAndDelete(id);
    }

    //Calcular galones despachados

    async calculateDispensedGallons(
        id:string
    ){
        const reading =
        await pumpReadingModel.findById(id);

        if(!reading){
            throw new Error("Lectura no encontrado");
        }

        reading.gallonsDispensed = reading.finalReading - reading.initialReading;
        
        await reading.save();
        return reading.gallonsDispensed;
    }

    //Calcular galones registrados

    async calculateSystemDispensed(
        id:string
    ){
        const reading =
             await pumpReadingModel.findById(id);

        if(!reading){
            throw new Error (
                "Lectura no encontrada"
            );
        }

        const requests = await fuelRequestModel.find({

            fuelPumpId:reading.fuelPumpId,
            createdAt:{
                $gte:reading.readingDate,
                $lte:new Date()
            }
        });

        const total = requests.reduce(
            (sum, request) =>
                sum + request.gallonQuantity,
            0
        );

        reading.systemDispensed = total;
        await reading.save();
        return total;
    }

    //Calcular diferencia

    async calculateDifference(
        id:string
    ){
        const reading = await pumpReadingModel.findById(id);

        if (!reading) {
            throw new Error(
                "Lectura no encontrada"
            );
        }

        reading.difference = reading.gallonsDispensed - reading.systemDispensed;

        await reading.save();
        return reading.difference;
    }

    //Actualizar estado

    async updateStatus(
        id:string
    ){
        const reading = await pumpReadingModel.findById(id);

        if (!reading) {
            throw new Error(
                "Lectura no encontrada"
            );
        }

        if(
            reading.difference === 0
        ){
            reading.status = "CUADRADA";
        }

        else{
            reading.status = "CON_DIFERENCIA"
        }

        await reading.save();
        return reading.status;
    }

    async updateFuelRequests(
        id:string
    ){
        const reading = await pumpReadingModel.findById(id);

        if(!reading){
            throw new Error(
                "Lectura no encontrada"
            );
        }

        const requests = await fuelRequestModel.find ({
            fuelPumpId:reading.fuelPumpId,
            createdAt:{
                $gte:reading.readingDate,
                $lte:new Date()

            }
        });

        for (const request of requests) {
            request.pumpReading =
            reading._id as any;

            request.pumpDifference = 
            reading.difference;
            await request.save();

        }

        return requests;
    }

    //Crear anomalía por diferencencia
    async creataDifferenceAnomaly(
        id:string
    ){
        const reading = await pumpReadingModel.findById(id);

        if(!reading){
           throw new Error (
            "Lectura no encontrada"
            );
        }

        if (reading.difference ===0){
            return null;
        }

        const anomaly = new anomalyModel({
            vehicleId:0,

            anomalyType:
            "DIFERENCIA EN BOMBA",

            description:
            `La bomba presenta una diferencia de ${reading.difference} galones`,

            detectedBy: "SISTEMA",

            status: "ABIERTA",

            comments: "Generada automáticamente por el sistema"
        });

        return await anomaly.save();
    }

    async closeReading(
        id:string
    ){

        const reading = await pumpReadingModel.findById(id);

        if (!reading){
            throw new Error (
                "Lectura no encontrada"
            );
        }

        await this.calculateDispensedGallons(id);

        await this.calculateSystemDispensed(id);

        const difference = await this.calculateDifference(id);

        await this.updateStatus(id);

        await this.updateFuelRequests(id);

        //Crear anomalía

        if(difference !== 0){
            await this.creataDifferenceAnomaly(id);
        }

        return await pumpReadingModel
        .findById(id)
        .populate("fuelPumpId")
        .populate("fuelTypeId")
        .populate("createdBy")
    }
}