//#region imports
import { DELETE, GET, POST, PUT, Query, TaonController } from 'taon/lib-prod';
import { ___NS__isInteger, UtilsOs__NS__isPortInUse } from 'tnp-core/lib-prod';
import { TaonBaseCliWorkerController } from '../classes/base-cli-worker';
import { Port } from './ports.entity';
//#endregion
let TaonPortsController = class TaonPortsController extends TaonBaseCliWorkerController {
    //#region fields
    START_PORT = 3000;
    END_PORT = 20000;
    portsCache = new Map();
    //#endregion
    //#region methods
    //#region methods / first free port
    get firstFreePort() {
        //#region  @backendFunc
        if (!this.portsCache) {
            return null;
        }
        return Array.from(this.portsCache.values()).find(p => p.status === 'unassigned');
        //#endregion
    }
    //#endregion
    //#region methods / get first unassigned port more than
    firstUnassignedPortMoreThan(port) {
        //#region   @backendFunc
        if (!this.portsCache) {
            return null;
        }
        return Array.from(this.portsCache.values()).find(p => p.status === 'unassigned' && p.port > port);
        //#endregion
    }
    //#endregion
    //#endregion
    //#region public methods
    //#region public methods / get
    //#region public methods / get / first free port
    getFirstFreePort() {
        return async () => {
            return this.firstFreePort;
        };
    }
    //#endregion
    //#region public methods / get / port by number
    getPortByNumber(portNumber) {
        return async () => {
            portNumber = Number(portNumber);
            return Array.from(this.portsCache.values()).find(f => f.port === portNumber);
        };
    }
    //#endregion
    //#region public methods / get / ports by status
    getPortsByStatus(status) {
        return async () => {
            return Array.from(this.portsCache.values())
                .filter(f => f.status === status)
                .sort(
            // sort by port number
            (a, b) => {
                if (a.port < b.port) {
                    return -1;
                }
                if (a.port > b.port) {
                    return 1;
                }
                return 0;
            });
        };
    }
    //#endregion
    //#endregion
    //#region public methods / ports take by os
    //#region public methods / ports take by os / DELETE port (make it unassigned)
    /**
     * make it unassigned
     */
    deletePort(portNumber) {
        //#region @backendFunc
        return async () => {
            portNumber = Number(portNumber);
            const repoPort = this.ctx.connection.getRepository(Port);
            let port = await repoPort.findOneBy({
                port: portNumber,
            });
            const oldServiceId = port.serviceId;
            port.status = 'unassigned';
            port.serviceId = Port.getTitleForFreePort(port.port);
            await repoPort.update({
                port: portNumber,
            }, port);
            port = await repoPort.findOneBy({
                port: portNumber,
            });
            this.portsCache.delete(oldServiceId);
            this.portsCache.set(port.serviceId, port);
            return port;
        };
        //#endregion
    }
    //#endregion
    //#region public methods / ports take by os / UPDATE port unique id
    /**
     * make it unassigned
     */
    updatePortUniqueId(portNumber, serviceId) {
        //#region @backendFunc
        return async () => {
            portNumber = Number(portNumber);
            serviceId = decodeURIComponent(serviceId);
            const repoPort = this.ctx.connection.getRepository(Port);
            let port = await repoPort.findOneBy({
                port: portNumber,
            });
            const oldServiceId = port.serviceId;
            port.serviceId = serviceId;
            await repoPort.update({
                port: portNumber,
            }, port);
            port = await repoPort.findOneBy({
                port: portNumber,
            });
            this.portsCache.delete(oldServiceId);
            this.portsCache.set(port.serviceId, port);
            return port;
        };
        //#endregion
    }
    //#endregion
    //#region public methods / ports take by os / ADD port take by os
    addTakeByOsPort(portNumber, uniqueId) {
        //#region @backendFunc
        return async () => {
            portNumber = Number(portNumber);
            uniqueId = decodeURIComponent(uniqueId);
            const repoPort = this.ctx.connection.getRepository(Port);
            let port = await repoPort.findOneBy({
                port: portNumber,
            });
            const oldServiceId = port.serviceId;
            port.serviceId = uniqueId;
            port.status = 'assigned-taken-by-os';
            await repoPort.update({
                port: portNumber,
            }, port);
            port = await repoPort.findOneBy({
                port: portNumber,
            });
            this.portsCache.delete(oldServiceId);
            this.portsCache.set(port.serviceId, port);
            return port;
        };
        //#endregion
    }
    //#endregion
    //#endregion
    //#region public methods / register and assign port
    /**
     * @param uniqueServiceName unique service name
     * @param startFrom start searching for free port from this number
     * @returns
     */
    registerAndAssignPort(uniqueServiceName, startFrom) {
        //#region @backendFunc
        return async () => {
            uniqueServiceName = decodeURIComponent(uniqueServiceName);
            if (this.portsCache.has(uniqueServiceName)) {
                return this.portsCache.get(uniqueServiceName);
            }
            const repo = this.ctx.connection.getRepository(Port);
            startFrom = Number(startFrom);
            const searchingWithStartPort = ___NS__isInteger(startFrom) &&
                startFrom >= this.START_PORT &&
                startFrom <= this.END_PORT;
            while (true) {
                const firstFreePort = (searchingWithStartPort &&
                    this.firstUnassignedPortMoreThan(startFrom)) ||
                    this.firstFreePort;
                if (!firstFreePort) {
                    // TODO wait and free up some ports
                    throw new Error('[taon] No free ports available');
                }
                const takenByOsPorts = Array.from(this.portsCache.values())
                    .filter(f => f.status === 'assigned-taken-by-os')
                    .map(f => f.port);
                if (takenByOsPorts.includes(firstFreePort.port)) {
                    continue;
                }
                if (await UtilsOs__NS__isPortInUse(firstFreePort.port)) {
                    firstFreePort.status = 'assigned-not-registered';
                    firstFreePort.serviceId = `taken by unregistered process (port: ${firstFreePort.port})`;
                    await repo.update(firstFreePort.port, firstFreePort);
                    continue;
                }
                firstFreePort.status = 'assigned';
                const oldServiceId = firstFreePort.serviceId;
                firstFreePort.serviceId = uniqueServiceName;
                firstFreePort.whenAssignedTimestamp = Date.now();
                this.portsCache.set(uniqueServiceName, firstFreePort);
                this.portsCache.delete(oldServiceId);
                await repo.update(firstFreePort.port, firstFreePort);
                return firstFreePort;
            }
        };
        //#endregion
    }
};
__decorate([
    GET(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "getFirstFreePort", null);
__decorate([
    GET(),
    __param(0, Query('portNumber')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number]),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "getPortByNumber", null);
__decorate([
    GET(),
    __param(0, Query('status')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String]),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "getPortsByStatus", null);
__decorate([
    DELETE(),
    __param(0, Query('portNumber')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number]),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "deletePort", null);
__decorate([
    PUT(),
    __param(0, Query('portNumber')),
    __param(1, Query('serviceId')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number, String]),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "updatePortUniqueId", null);
__decorate([
    POST(),
    __param(0, Query('portNumber')),
    __param(1, Query('uniqueId')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number, String]),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "addTakeByOsPort", null);
__decorate([
    PUT(),
    __param(0, Query('uniqueServiceName')),
    __param(1, Query('startFrom')),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Number]),
    __metadata("design:returntype", Object)
], TaonPortsController.prototype, "registerAndAssignPort", null);
TaonPortsController = __decorate([
    TaonController({
        className: 'TaonPortsController',
    })
], TaonPortsController);
export { TaonPortsController };
//# sourceMappingURL=c:/Users/darek/projects/npm/taon-dev/tnp-helpers/dist-nocutsrc-prod/lib/base/tcp-udp-ports/ports.controller.js.map