"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TripWithLastPositionRepository = void 0;
const const_1 = require("../../../../schema-definitions/const");
const models_1 = require("../../../../schema-definitions/vehicle-descriptors/models");
const vehicle_positions_1 = require("../../../../schema-definitions/vehicle-positions");
const views_1 = require("../../../../schema-definitions/vehicle-positions/models/views");
const output_gateway_1 = require("@golemio/core/dist/output-gateway");
const Geo_1 = require("@golemio/core/dist/output-gateway/Geo");
const ioc_1 = require("@golemio/core/dist/output-gateway/ioc/");
const golemio_errors_1 = require("@golemio/core/dist/shared/golemio-errors");
const moment_timezone_1 = __importDefault(require("@golemio/core/dist/shared/moment-timezone"));
const sequelize_1 = __importStar(require("@golemio/core/dist/shared/sequelize"));
const const_2 = require("../../../../const");
const PositionOutputMapper_1 = require("../helpers/PositionOutputMapper");
class TripWithLastPositionRepository extends output_gateway_1.SequelizeModel {
    constructor() {
        super("TripWithLastPositionRepository", views_1.TripWithLastPositionModel.tableName, views_1.TripWithLastPositionModel.attributeModel, {
            schema: const_1.PG_SCHEMA,
        });
        this.Associate = (repositories) => {
            this.sequelizeModel.hasMany(repositories.processedPositionRepository.sequelizeModel, {
                as: "all_positions",
                foreignKey: "trips_id",
                scope: {
                    lat: { [sequelize_1.Op.not]: null },
                    lng: { [sequelize_1.Op.not]: null },
                },
            });
            this.sequelizeModel.belongsTo(repositories.vehicleTypeRepository.sequelizeModel, {
                as: "vehicle_type",
                foreignKey: "vehicle_type_id",
            });
            this.sequelizeModel.belongsTo(repositories.vehicleDescriptorRepository.sequelizeModel, {
                as: "vehicle_descriptor",
                foreignKey: "vehicle_registration_number",
                targetKey: "registration_number",
                scope: {
                    [sequelize_1.default.Op.and]: [
                        sequelize_1.default.literal(`${views_1.TripWithLastPositionModel.tableName}.gtfs_route_type = vehicle_descriptor.gtfs_route_type`),
                    ],
                },
            });
        };
        /** Retrieves all vehicle trips
         * @param {object} options Options object with params
         * @param {number} [options.limit] Limit
         * @param {number} [options.offset] Offset
         * @param {string} [options.routeId] Filter trips by specific route id
         * @param {string} [options.routeShortName] Filter trips by specific route short name
         * @param {string} [options.tripId] Filter trips by specific trip id
         * @param {boolean} [options.includeNotTracking] Should include not tracking vehicle positions (those off a trip)
         * @returns Array of the retrieved records
         */
        this.GetAll = async (options) => {
            const { limit, offset, updatedSince } = options;
            const minUpdatedAt = (0, moment_timezone_1.default)().subtract(10, "minutes");
            // updatedSince cannot be more than 10 minutes ago
            const maxUpdatedAt = updatedSince && (0, moment_timezone_1.default)(updatedSince).isSameOrAfter(minUpdatedAt) ? updatedSince.toISOString() : null;
            const include = this.composeIncludes(options);
            try {
                const rows = await this.sequelizeModel.findAll({
                    attributes: {
                        exclude: ["id", "vehicle_type_id"],
                    },
                    include,
                    limit,
                    offset,
                    where: {
                        [sequelize_1.Op.and]: [
                            {
                                state_position: {
                                    [sequelize_1.Op.in]: this.determinePossibleStatePositions(options.includeNotTracking, options.includeNotPublic),
                                },
                            },
                            {
                                [sequelize_1.Op.or]: [
                                    { gtfs_trip_id: { [sequelize_1.Op.ne]: null } },
                                    {
                                        vehicle_registration_number: { [sequelize_1.Op.in]: const_2.ArrayNotPublicRegistrationNumbers },
                                    },
                                ],
                            },
                            maxUpdatedAt
                                ? {
                                    updated_at: {
                                        [sequelize_1.Op.gt]: maxUpdatedAt,
                                    },
                                }
                                : {},
                            options.cisTripNumber ? { cis_trip_number: options.cisTripNumber } : {},
                            options.routeId ? { gtfs_route_id: options.routeId } : {},
                            options.routeShortName ? { gtfs_route_short_name: options.routeShortName } : {},
                            options.tripId ? { gtfs_trip_id: options.tripId } : {},
                        ],
                    },
                });
                if (rows.length === 0) {
                    return {
                        data: (0, Geo_1.buildGeojsonFeatureCollection)([]),
                        metadata: {
                            maxUpdatedAt: maxUpdatedAt || new Date().toISOString(),
                        },
                    };
                }
                let returnData = [];
                let maxUpdatedAtData = null;
                for (const row of rows) {
                    returnData.push(PositionOutputMapper_1.PositionOutputMapper.mapTripModelToDto(row));
                    if (maxUpdatedAtData === null || row.updated_at > maxUpdatedAtData) {
                        maxUpdatedAtData = row.updated_at;
                    }
                }
                return {
                    data: (0, Geo_1.buildGeojsonFeatureCollection)(returnData),
                    metadata: {
                        maxUpdatedAt: maxUpdatedAtData?.toISOString() || new Date().toISOString(),
                    },
                };
            }
            catch (err) {
                throw new golemio_errors_1.GeneralError("Database error", "VehiclepositionsTripsModel", err, 500);
            }
        };
        /** Retrieves specific vehicle trip
         * @param {string} id Id of the trip
         * @param {object} [options] Options object with params
         * @param {string} [options.includeNotTracking] Returns last known trip even if it is not tracked at time
         * @param {boolean} [options.includePositions] Should include all vehicle positions
         * @returns Object of the retrieved record or null
         */
        this.GetOneByGTFSTripId = async (id, options) => {
            try {
                const include = this.composeIncludes(options);
                const data = await this.sequelizeModel.findOne({
                    attributes: {
                        exclude: ["id", "vehicle_type_id"],
                        include: ["updated_at"],
                    },
                    include,
                    where: {
                        gtfs_trip_id: id,
                    },
                    order: [["updated_at", "DESC"]],
                });
                if (!data) {
                    return null;
                }
                if (data.lat === null || data.lng === null) {
                    throw new golemio_errors_1.GeneralError("Missing lat or lng", this.constructor.name, undefined, 500, "pid");
                }
                return PositionOutputMapper_1.PositionOutputMapper.mapTripModelToDto(data);
            }
            catch (err) {
                throw new golemio_errors_1.GeneralError("Database error", "VehiclePositionsTripsModel", err, 500);
            }
        };
        this.composeIncludes = (options) => {
            const possibleStatePositions = this.determinePossibleStatePositions(options.includeNotTracking, options.includeNotPublic);
            const include = [
                {
                    as: "vehicle_type",
                    model: this.dbConnector.getConnection().models[vehicle_positions_1.VehiclePositions.vehicleTypes.pgTableName],
                },
                {
                    as: "vehicle_descriptor",
                    model: this.dbConnector.getConnection().models[models_1.DescriptorModel.tableName],
                    attributes: ["is_air_conditioned", "has_usb_chargers"],
                },
            ];
            if (options.includePositions) {
                include.push({
                    attributes: {
                        exclude: ["trips_id", "state_position"],
                    },
                    as: "all_positions",
                    model: this.dbConnector.getConnection().models[views_1.ProcessedPositionModel.tableName],
                    where: {
                        state_position: {
                            [sequelize_1.Op.in]: possibleStatePositions,
                        },
                    },
                });
            }
            return include;
        };
        this.determinePossibleStatePositions = (includeNotTracking, includeNotPublic) => {
            const possibleStatePosition = [const_2.StatePositionEnum.AT_STOP, const_2.StatePositionEnum.ON_TRACK];
            if (includeNotTracking) {
                possibleStatePosition.push(const_2.StatePositionEnum.AFTER_TRACK, const_2.StatePositionEnum.AFTER_TRACK_DELAYED, const_2.StatePositionEnum.BEFORE_TRACK, const_2.StatePositionEnum.BEFORE_TRACK_DELAYED, const_2.StatePositionEnum.CANCELED, const_2.StatePositionEnum.OFF_TRACK);
            }
            if (includeNotPublic) {
                possibleStatePosition.push(const_2.StatePositionEnum.NOT_PUBLIC);
            }
            return possibleStatePosition;
        };
        this.dbConnector = ioc_1.OutputGatewayContainer.resolve(ioc_1.ContainerToken.PostgresDatabase);
    }
    GetOne() {
        throw new Error("Not implemented");
    }
}
exports.TripWithLastPositionRepository = TripWithLastPositionRepository;
//# sourceMappingURL=TripWithLastPositionRepository.js.map