'use strict';

const { Client4 } = require('@mattermost/client');
const { v4: uuidV4 } = require('uuid');
const TokenManager = require('./tokenManager');
const Logger = require('./logs');
const log = new Logger('Mattermost');

// Token operations
class TokenService {
    constructor(secret, expiresIn, encryptionKey) {
        this.tokenManager = new TokenManager(secret, expiresIn, encryptionKey);
    }

    createToken(payload) {
        return this.tokenManager.create(payload, true);
    }

    decodeToken(token) {
        return this.tokenManager.decodePayload(token);
    }
}

// Mattermost authentication
class MattermostAuthService {
    constructor({ serverUrl, username, password }) {
        this.client = new Client4();
        this.client.setUrl(serverUrl);
        this.username = username;
        this.password = password;
    }

    async login() {
        try {
            const user = await this.client.login(this.username, this.password);
            log.debug('Logged into Mattermost as', user.username);
        } catch (error) {
            log.error('Failed to log into Mattermost:', error);
        }
    }
}

// Meeting-related operations
class MeetingService {
    constructor(tokenService, serverUrl, disabledEndpoints = [], secure = false) {
        this.tokenService = tokenService;
        this.serverUrl = serverUrl;
        this.disabledEndpoints = disabledEndpoints;
        this.secure = secure;
    }

    isEndpointDisabled(endpoint) {
        return this.disabledEndpoints.includes(endpoint);
    }

    createTokenPayload(userId) {
        return {
            userId,
            roomId: uuidV4(),
            timestamp: Date.now(),
        };
    }

    createMeetingURL(req) {
        return `${this.getBaseURL(req)}/join/${uuidV4()}`;
    }

    createSecureMeetingURL(req, userId) {
        const payload = this.createTokenPayload(userId);
        const token = this.tokenService.createToken(payload);
        return {
            url: `${this.getBaseURL(req)}/mattermost/join/${encodeURIComponent(token)}`,
            payload,
        };
    }

    decodeToken(token) {
        return this.tokenService.decodeToken(token);
    }

    getBaseURL(req) {
        const host = req.headers.host;
        const protocol = host.includes('localhost') ? 'http' : 'https';
        return `${protocol}://${host}`;
    }
}

// Just handles routing and delegates everything
class MattermostController {
    constructor(app, config, htmlInjector, clientHtml) {
        try {
            if (!config || !config.enabled) {
                log.info('MattermostController is disabled due to configuration');
                return;
            }

            this.validateConfig(config);

            const tokenService = new TokenService(
                config.token || 'fallback-secret-at-least-32-chars',
                config.roomTokenExpire || '15m',
                config.encryptionKey || 'fallback-encryption-key-32chars'
            );

            this.authService = new MattermostAuthService({
                serverUrl: config.server_url,
                username: config.username,
                password: config.password,
            });

            this.meetingService = new MeetingService(
                tokenService,
                config.server_url,
                config.api_disabled,
                config.security
            );

            this.token = config.token;
            this.app = app;
            this.htmlInjector = htmlInjector;
            this.clientHtml = clientHtml;

            this.authService.login();
            this.setupRoutes();
        } catch (error) {
            log.error('MattermostController disabled due to config error:', error.message);
        }
    }

    validateConfig(cfg) {
        if (!cfg.enabled || !cfg.server_url || !cfg.token || !cfg.username || !cfg.password) {
            throw new Error('Invalid Mattermost configuration');
        }
    }

    setupRoutes() {
        this.app.post('/mattermost', (req, res) => {
            if (this.meetingService.isEndpointDisabled('mattermost')) {
                return res.end('`This endpoint has been disabled`. Please contact the administrator.');
            }

            const { token, text, command, channel_id, user_id } = req.body;
            if (token !== this.token) {
                log.error('Invalid token attempt', { token });
                return res.status(403).send('Invalid token');
            }

            if (this.isP2PCommand(command, text)) {
                try {
                    const meetingUrl = this.generateMeetingUrl(req, user_id);
                    const message = this.getMeetingResponseMessage(meetingUrl);

                    return res.json({
                        response_type: 'in_channel',
                        text: message,
                        channel_id,
                    });
                } catch (error) {
                    log.error('Meeting creation failed', { error, user_id });
                    return res.status(500).json({ error: 'Failed to create meeting' });
                }
            }

            return res.status(404).send('Command not recognized');
        });

        this.app.get('/mattermost/join/:roomToken', (req, res) => {
            if (this.meetingService.isEndpointDisabled('mattermost')) {
                return res.end('This endpoint has been disabled');
            }

            const { roomToken } = req.params;

            if (!roomToken) {
                return res.status(401).send('Token required');
            }

            try {
                const payload = this.meetingService.decodeToken(roomToken);
                if (!payload || !payload.userId || !payload.roomId) {
                    log.error('Invalid or malformed token payload', payload);
                    return res.status(400).send('Invalid token');
                }

                log.debug('Decoded payload', payload);
                return this.htmlInjector.injectHtml(this.clientHtml, res);
            } catch (error) {
                log.error('Token processing error', {
                    error: error.message,
                    token: roomToken.substring(0, 20) + '...',
                });
                return res.status(500).send('Error processing token');
            }
        });
    }

    isP2PCommand(command, text) {
        const normalizedCommand = command?.trim();
        const normalizedText = text?.trim();
        return normalizedCommand === '/p2p' || normalizedText === '/p2p';
    }

    generateMeetingUrl(req, userId) {
        if (this.meetingService.secure) {
            return this.meetingService.createSecureMeetingURL(req, userId).url;
        }
        return this.meetingService.createMeetingURL(req);
    }

    getMeetingResponseMessage(meetingUrl) {
        return this.meetingService.secure
            ? `🔒 [Join your secure private meeting](${meetingUrl})`
            : `🌐 Join meeting: ${meetingUrl}`;
    }
}

module.exports = MattermostController;
