const path = require('path');
const fs = require('fs');
const { randomUUID } = require('crypto');
const { WebSocketServer } = require('ws');
const pino = require('pino');
const qrcode = require('qrcode-terminal');
const mime = require('mime-types');

const {
    default: makeWASocket,
    DisconnectReason,
    useMultiFileAuthState,
    fetchLatestBaileysVersion,
    downloadMediaMessage,
} = require('@whiskeysockets/baileys');

let _sendQueue = Promise.resolve();

function enqueueSend(fn) {
    _sendQueue = _sendQueue.then(() => fn()).catch((err) => {
        console.error('[bridge] erro na fila de envio:', err);
    });
}

const PORT = Number(process.env.WHATSAPP_BRIDGE_PORT || 3001);
const HOST = process.env.WHATSAPP_BRIDGE_HOST || '127.0.0.1';
const SESSION_DIR = process.env.WHATSAPP_SESSION_DIR || path.join(__dirname, 'session');
const TMP_DIR = process.env.WHATSAPP_TMP_DIR || path.join(__dirname, 'tmp');

fs.mkdirSync(SESSION_DIR, { recursive: true });
fs.mkdirSync(TMP_DIR, { recursive: true });

const wss = new WebSocketServer({ host: HOST, port: PORT });
const wsClients = new Set();
let currentSock = null;

wss.on('connection', (ws) => {
    wsClients.add(ws);
    ws.on('close', () => wsClients.delete(ws));
    ws.on('error', () => wsClients.delete(ws));
});

function broadcastToPython(payload) {
    const data = JSON.stringify(payload);
    for (const client of wsClients) {
        if (client.readyState === 1) {
            client.send(data);
        }
    }
}

async function sendToWhatsApp(payload) {
    if (!currentSock) {
        return;
    }

    if (payload.action === 'send_text') {
        const to = String(payload.to || '');
        const text = String(payload.text || '');
        if (to && text) {
            await currentSock.sendMessage(to, { text });
        }
        return;
    }

    if (payload.action === 'send_buttons') {
        const to = String(payload.to || '');
        const text = String(payload.text || '');
        const buttonsInput = Array.isArray(payload.buttons) ? payload.buttons : [];
        const buttons = buttonsInput
            .map((button) => ({
                buttonId: String(button.id || ''),
                buttonText: { displayText: String(button.text || '') },
                type: 1,
            }))
            .filter((button) => button.buttonId && button.buttonText.displayText)
            .slice(0, 3);

        if (to && text && buttons.length > 0) {
            await currentSock.sendMessage(to, {
                text,
                buttons,
                headerType: 1,
            });
        }
        return;
    }

    if (payload.action === 'send_document') {
        const to = String(payload.to || '');
        const filename = String(payload.filename || 'file');
        const mimetype = String(payload.mimetype || 'application/octet-stream');
        const dataB64 = String(payload.data_base64 || '');
        if (to && dataB64) {
            const buffer = Buffer.from(dataB64, 'base64');
            await currentSock.sendMessage(to, {
                document: buffer,
                mimetype,
                fileName: filename,
            });
        }
        return;
    }

    if (payload.action === 'send_typing') {
        const to = String(payload.to || '');
        if (to) {
            await currentSock.sendPresenceUpdate('composing', to);
        }
    }
}

wss.on('connection', (ws) => {
    ws.on('message', async (data) => {
        let payload;
        try {
            payload = JSON.parse(String(data));
        } catch {
            return;
        }

        enqueueSend(() => sendToWhatsApp(payload));
    });
});

function readText(message) {
    if (!message) {
        return null;
    }

    if (typeof message.conversation === 'string') {
        return message.conversation;
    }
    if (typeof message.extendedTextMessage?.text === 'string') {
        return message.extendedTextMessage.text;
    }
    if (typeof message.imageMessage?.caption === 'string') {
        return message.imageMessage.caption;
    }
    if (typeof message.videoMessage?.caption === 'string') {
        return message.videoMessage.caption;
    }
    return null;
}

function readButtonSelection(message) {
    const idFromButtons = message?.buttonsResponseMessage?.selectedButtonId;
    if (idFromButtons) {
        return String(idFromButtons);
    }

    const idFromTemplate = message?.templateButtonReplyMessage?.selectedId;
    if (idFromTemplate) {
        return String(idFromTemplate);
    }

    const paramsJson = message?.interactiveResponseMessage?.nativeFlowResponseMessage?.paramsJson;
    if (!paramsJson) {
        return null;
    }

    try {
        const parsed = JSON.parse(paramsJson);
        return String(parsed?.id || parsed?.selectedId || parsed?.button_id || '');
    } catch {
        return null;
    }
}

function firstMediaMessage(message) {
    if (message?.imageMessage) {
        return { key: 'imageMessage', value: message.imageMessage };
    }
    if (message?.videoMessage) {
        return { key: 'videoMessage', value: message.videoMessage };
    }
    if (message?.audioMessage) {
        return { key: 'audioMessage', value: message.audioMessage };
    }
    if (message?.documentMessage) {
        return { key: 'documentMessage', value: message.documentMessage };
    }
    if (message?.stickerMessage) {
        return { key: 'stickerMessage', value: message.stickerMessage };
    }
    return null;
}

async function saveMedia(message, logger) {
    const media = firstMediaMessage(message);
    if (!media) {
        return null;
    }

    const mimeType = media.value?.mimetype || 'application/octet-stream';
    const ext = mime.extension(mimeType) || 'bin';
    const filename = `${Date.now()}_${randomUUID()}.${ext}`;
    const absolutePath = path.resolve(TMP_DIR, filename);

    const mediaBuffer = await downloadMediaMessage(
        { message },
        'buffer',
        {},
        {
            logger,
            reuploadRequest: undefined,
        }
    );

    fs.writeFileSync(absolutePath, mediaBuffer);
    return {
        media_path: absolutePath,
        mime_type: mimeType,
        media_type: media.key,
    };
}

async function startSocket() {
    const { state, saveCreds } = await useMultiFileAuthState(SESSION_DIR);
    const { version } = await fetchLatestBaileysVersion();

    const logger = pino({ level: process.env.BAILEYS_LOG_LEVEL || 'warn' });

    const sock = makeWASocket({
        auth: state,
        version,
        logger,
        printQRInTerminal: false,
        syncFullHistory: false,
        markOnlineOnConnect: true,
        shouldIgnoreJid: (jid) =>
            typeof jid === 'string'
            && (jid.endsWith('@g.us') || jid.endsWith('@broadcast') || jid === 'status@broadcast'),
    });
    currentSock = sock;

    sock.ev.on('creds.update', saveCreds);

    sock.ev.on('connection.update', (update) => {
        const { connection, lastDisconnect, qr } = update;

        if (qr) {
            qrcode.generate(qr, { small: true });
            console.log('[bridge] Escaneie o QR acima no WhatsApp.');
        }

        if (connection === 'open') {
            console.log('[bridge] Conectado ao WhatsApp.');
        }

        if (connection === 'close') {
            if (currentSock === sock) {
                currentSock = null;
            }
            const code = lastDisconnect?.error?.output?.statusCode;
            const shouldReconnect = code !== DisconnectReason.loggedOut;
            console.log(`[bridge] Conexão encerrada (code=${code}). Reconnect=${shouldReconnect}`);
            if (shouldReconnect) {
                setTimeout(() => startSocket().catch((err) => console.error('[bridge] reconnect error', err)), 1500);
            }
        }
    });

    sock.ev.on('messages.upsert', async (upsert) => {
        if (upsert.type !== 'notify') {
            return;
        }

        for (const incoming of upsert.messages || []) {
            if (!incoming.message || incoming.key?.fromMe) {
                continue;
            }

            const sender = incoming.key?.remoteJid;
            if (!sender) {
                continue;
            }

            const selectedId = readButtonSelection(incoming.message);
            if (selectedId) {
                broadcastToPython({
                    type: 'message',
                    sender,
                    text: selectedId,
                    is_button_reply: true,
                    message_id: incoming.key?.id || null,
                });
                continue;
            }

            const text = readText(incoming.message);
            if (text && text.trim().length > 0) {
                broadcastToPython({
                    type: 'message',
                    sender,
                    text,
                    message_id: incoming.key?.id || null,
                });
                continue;
            }

            try {
                const mediaPayload = await saveMedia(incoming.message, logger);
                if (mediaPayload) {
                    broadcastToPython({
                        type: 'message',
                        sender,
                        ...mediaPayload,
                        text: readText(incoming.message) || '',
                        message_id: incoming.key?.id || null,
                    });
                }
            } catch (error) {
                console.error('[bridge] Erro ao baixar mídia:', error);
            }
        }
    });

}

startSocket().catch((error) => {
    console.error('[bridge] erro fatal', error);
    process.exit(1);
});

console.log(`[bridge] WebSocket escutando em ws://${HOST}:${PORT}`);
