"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SaveDataToDBTask = void 0;
const CoreToken_1 = require("@golemio/core/dist/helpers/ioc/CoreToken");
const ioc_1 = require("@golemio/core/dist/integration-engine/ioc");
const queueprocessors_1 = require("@golemio/core/dist/integration-engine/queueprocessors");
const workers_1 = require("@golemio/core/dist/integration-engine/workers");
const golemio_errors_1 = require("@golemio/core/dist/shared/golemio-errors");
const CisStopRepository_1 = require("../data-access/CisStopRepository");
const TripsRepository_1 = require("../data-access/TripsRepository");
const TripsMapper_1 = __importDefault(require("../data-access/helpers/TripsMapper"));
const SaveDataToDBSchema_1 = require("../schema/SaveDataToDBSchema");
const MpvMessageTransformation_1 = require("../transformations/MpvMessageTransformation");
class SaveDataToDBTask extends workers_1.AbstractTask {
    constructor(queuePrefix) {
        super(queuePrefix);
        this.queueName = "saveDataToDB";
        this.queueTtl = 5 * 60 * 1000; // 5 minutes
        this.schema = SaveDataToDBSchema_1.SaveDataToDBValidationSchema;
        this.tripsRepository = new TripsRepository_1.TripsRepository();
        this.cisStopRepository = new CisStopRepository_1.CisStopRepository();
        this.messageTransformation = new MpvMessageTransformation_1.MpvMessageTransformation();
        this.logger = ioc_1.IntegrationEngineContainer.resolve(CoreToken_1.CoreToken.Logger);
    }
    async execute(data, messageProperties) {
        const taskStartTimestamp = new Date().getTime();
        const { trips, positions, stops } = await this.messageTransformation.transform(data.m.spoj);
        const tripEntities = await this.tripsRepository.bulkSave(trips, 
        // Ignore incoming start_timestamp for existing trips to avoid overwriting existing computed timestamp
        // TODO this is a workaround, dedicated column (gtfs_start_timestamp) will be added
        Object.keys(trips[0] ?? {})
            .filter((key) => key !== "start_timestamp")
            .concat("updated_at"), true);
        const { inserted, updated } = TripsMapper_1.default.mapTripEntitiesToUpdateDto(tripEntities, taskStartTimestamp);
        // At this point we want to send ACK to rabbit and let the rest of the processing to be done in the background
        await this.sendDataToUpdateGtfsTripId(inserted, positions, messageProperties?.timestamp);
        await this.sendDataToUpdateDelay(updated, positions, messageProperties?.timestamp);
        // Save cis stops to later propagate platform codes to departure boards
        await this.saveStops(stops);
    }
    async saveStops(stops) {
        try {
            await this.cisStopRepository.bulkSave(stops);
        }
        catch (error) {
            if (error instanceof golemio_errors_1.AbstractGolemioError) {
                this.logger.error(error);
            }
            else {
                this.logger.error(new golemio_errors_1.GeneralError("Error while saving stops", SaveDataToDBTask.name, error, undefined, "pid", this.queueName));
            }
        }
    }
    async sendDataToUpdateGtfsTripId(insertedTrips, positions, firstMessageCreatedAt) {
        try {
            // send message for update GTFSTripIds
            for (let i = 0, chunkSize = 50; i < insertedTrips.length; i += chunkSize) {
                const insertedBatch = insertedTrips.slice(i, i + chunkSize);
                const insertedIds = insertedBatch.map((ins) => {
                    return ins.id;
                });
                await queueprocessors_1.QueueManager.sendMessageToExchange(this.queuePrefix, "updateGTFSTripId", {
                    trips: insertedBatch,
                    positions: positions.filter((position) => insertedIds.includes(position.trips_id)),
                }, {
                    timestamp: firstMessageCreatedAt,
                });
            }
        }
        catch (error) {
            if (error instanceof golemio_errors_1.AbstractGolemioError) {
                this.logger.error(error);
            }
            else {
                this.logger.error(new golemio_errors_1.GeneralError("Error while sending data to update GTFS trip id", SaveDataToDBTask.name, error, undefined, "pid", this.queueName));
            }
        }
    }
    async sendDataToUpdateDelay(updatedTrips, positions, firstMessageCreatedAt) {
        try {
            // aggregate runs by origin_route_name & run_number to keep related messages in same bulk
            const runTripMap = updatedTrips.length ? { unknown: [] } : {};
            for (const trip of updatedTrips) {
                if (!trip.origin_route_name || !trip.run_number) {
                    runTripMap.unknown.push(trip.id);
                }
                else {
                    const key = `${trip.origin_route_name}_${trip.run_number}`;
                    runTripMap[key] ? runTripMap[key].push(trip.id) : (runTripMap[key] = [trip.id]);
                }
            }
            const chunks = [];
            let idx = 0;
            for (const key in runTripMap) {
                if (!chunks[idx]) {
                    chunks[idx] = [];
                }
                chunks[idx] = chunks[idx].concat(runTripMap[key]);
                if (chunks[idx].length > 200) {
                    idx++;
                }
            }
            const promises = [];
            for (const key in chunks) {
                promises.push(this.getTripDataAndSendToUpdateDelay(chunks[key], positions, firstMessageCreatedAt));
            }
            await Promise.all(promises);
        }
        catch (error) {
            if (error instanceof golemio_errors_1.AbstractGolemioError) {
                this.logger.error(error);
            }
            else {
                this.logger.error(new golemio_errors_1.GeneralError("Error while sending data to update delay", SaveDataToDBTask.name, error, undefined, "pid", this.queueName));
            }
        }
    }
    async getTripDataAndSendToUpdateDelay(chunk, positions, firstMessageCreatedAt) {
        try {
            const updatedTrips = await this.tripsRepository.findAllAssocTripIds(chunk);
            await queueprocessors_1.QueueManager.sendMessageToExchange(this.queuePrefix, "updateDelay", {
                updatedTrips,
                positions: positions.filter((position) => chunk.includes(position.trips_id)),
            }, {
                timestamp: firstMessageCreatedAt,
            });
        }
        catch (error) {
            if (error instanceof golemio_errors_1.AbstractGolemioError) {
                this.logger.error(error);
            }
            else {
                this.logger.error(new golemio_errors_1.GeneralError("Error while sending data to update delay", SaveDataToDBTask.name, error, undefined, "pid", this.queueName));
            }
        }
    }
}
exports.SaveDataToDBTask = SaveDataToDBTask;
//# sourceMappingURL=SaveDataToDBTask.js.map