/*
 * *****************************************************************************
 * Copyright (C) 2019-2021 Chrystian Huot <chrystian.huot@saubeo.solutions>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 * ****************************************************************************
 */

'use strict';

import EventEmitter from 'events';
import FormData from 'form-data';
import { URL } from 'url';

import { Log } from './log.js';

export class Downstream {
    constructor(ctx) {
        this.config = ctx.config;

        this.log = ctx.log;

        this.config.downstreams.forEach((downstream) => {
            if (typeof downstream.apiKey !== 'string' || !downstream.apiKey.length) {
                this.log.write(Log.error, 'Config: no dirWatch.apiKey defined');
            }

            if (typeof downstream.disabled !== 'boolean') {
                downstream.disabled = false;
            }

            if (downstream.systems === undefined) {
                downstream.systems = '*';
            }

            if (typeof downstream.url !== 'string' || !downstream.url.length) {
                this.log.write(Log.error, 'Config: no dirWatch.url defined');
            }
        });

        if (ctx.controller instanceof EventEmitter) {
            ctx.controller.on('call', (call) => this.exportCall(call));
        }
    }

    exportCall(call) {
        const parseSystem = (system) => {
            const parseTalkgroup = (talkgroup) => {
                if (Array.isArray(talkgroup)) {
                    return talkgroup.some((tg) => parseTalkgroup(tg));

                } else if (talkgroup !== null && typeof talkgroup === 'object') {
                    return talkgroup.id === call.talkgroup;

                } else if (typeof talkgroup === 'number') {
                    return talkgroup === call.talkgroup;

                } else {
                    return talkgroup === undefined || talkgroup === '*';
                }
            };

            if (Array.isArray(system)) {
                return system.some((sys) => parseSystem(sys));

            } else if (system !== null && typeof system === 'object' && system.id === call.system) {
                return parseTalkgroup(system.talkgroups);

            } else if (typeof system === 'number') {
                return system === call.system;

            } else if (typeof system === 'string') {
                return system === '*';

            } else {
                return false;
            }
        };

        this.config.downstreams.forEach((downstream) => {
            if (typeof downstream.disabled === 'boolean' && downstream.disabled) {
                return;
            }

            if (typeof downstream.apiKey !== 'string' || !downstream.apiKey.length) {
                return;
            }

            if (typeof downstream.url !== 'string' || !downstream.url.length) {
                return;
            }

            if (parseSystem(downstream.systems)) {
                switch (downstream.type) {
                    default:
                        this.exportCallToRdioScanner(call, downstream);
                }
            }
        });
    }

    exportCallToRdioScanner(call, downstream) {
        const apiUrl = new URL('/api/call-upload', downstream.url);

        const form = new FormData();

        form.append('key', downstream.apiKey);
        form.append('audio', call.audio, {
            contentType: call.audioType,
            filename: call.audioName,
            knownLength: call.audio.length,
        });
        form.append('dateTime', call.dateTime.toJSON());
        form.append('frequencies', JSON.stringify(call.frequencies));

        if (typeof call.frequency === 'number') {
            form.append('frequency', call.frequency);
        }

        if (typeof call.source === 'number') {
            form.append('source', call.source);
        }

        form.append('sources', JSON.stringify(call.sources));
        form.append('system', this.getSystemId(call, downstream));
        form.append('talkgroup', this.getTalkgroupId(call, downstream));

        form.submit({
            host: apiUrl.hostname,
            path: apiUrl.pathname,
            port: apiUrl.port,
            protocol: apiUrl.protocol,
            rejectUnauthorized: false,
        }, (error, response) => {
            const message = `Downstream: system=${call.system} talkgroup=${call.talkgroup} file=${call.audioName} to=${downstream.url}`;

            if (error) {
                this.log.write(Log.error, `Downstream: ${message} ${error.message}`);

            } else if (response.statusCode !== 200) {
                this.log.write(Log.warn, `Downstream: ${message} ${response.statusMessage}`);

            } else {
                this.log.write(Log.info, `Downstream: ${message} Success`);
            }
        });
    }

    getSystemId(call, downstream) {
        const parseSystem = (system) => {
            if (Array.isArray(system)) {
                system = system.find((sys) => sys !== null && typeof sys === 'object' && sys.id === call.system);

                return system ? parseSystem(system) : call.system;

            } else if (system !== null && typeof system === 'object') {
                return typeof system.id_as === 'number' && system.id === call.system ? system.id_as : call.system;

            } else {
                return call.system;
            }
        };

        return parseSystem(downstream.systems);
    }

    getTalkgroupId(call, downstream) {
        const parseSystem = (system) => {
            const parseTalkgroup = (talkgroup) => {
                if (Array.isArray(talkgroup)) {
                    talkgroup = talkgroup.find((tg) => tg !== null && typeof tg === 'object' && tg.id === call.talkgroup);

                    return talkgroup ? parseTalkgroup(talkgroup) : call.talkgroup;

                } else if (talkgroup !== null && typeof talkgroup === 'object') {
                    return typeof talkgroup.id_as === 'number' && talkgroup.id === call.talkgroup ? talkgroup.id_as : talkgroup.id;

                } else {
                    return call.talkgroup;
                }
            };

            if (Array.isArray(system)) {
                system = system.find((sys) => sys !== null && typeof sys === 'object' && sys.id === call.system);

                return system ? parseSystem(system) : call.talkgroup;

            } else if (system !== null && typeof system === 'object') {
                return system.id === call.system ? parseTalkgroup(system.talkgroups) : call.talkgroup;

            } else {
                return call.talkgroup;
            }
        };

        return parseSystem(downstream.systems);
    }
}
