const { ProviderClass } = require('@bot-whatsapp/bot')
const pino = require('pino')
const mime = require('mime-types')
const fs = require('fs')
const {
    default: makeWASocket,
    useMultiFileAuthState,
} = require('@adiwajshing/baileys')
const {
    baileyGenerateImage,
    baileyCleanNumber,
    baileyIsValidNumber,
} = require('./utils')

/**
 * ⚙️ BaileysProvider: Es una clase tipo adaptor
 * que extiende clases de ProviderClass (la cual es como interfaz para sber que funciones rqueridas)
 * https://github.com/adiwajshing/Baileys
 */
class BaileysProvider extends ProviderClass {
    vendor
    constructor() {
        super()
        this.initBailey().then(() => this.initBusEvents())
    }

    /**
     * Iniciar todo Bailey
     */
    async initBailey() {
        const { state, saveCreds } = await useMultiFileAuthState('sessions')

        try {
            this.vendor = makeWASocket({
                printQRInTerminal: false,
                auth: state,
                logger: pino({ level: 'error' }),
            })

            this.vendor.ev.on(
                'connection.update',
                async ({ qr, connection, lastDisconnect }) => {
                    if (qr) baileyGenerateImage(qr)
                    if (connection === 'open') this.emit('ready', true)
                    if (lastDisconnect?.error) {
                        saveCreds()
                        this.initBailey()
                    }
                }
            )
        } catch (e) {
            this.emit('error', e)
        }
    }

    /**
     * Mapeamos los eventos nativos a los que la clase Provider espera
     * para tener un standar de eventos
     * @returns
     */
    busEvents = () => [
        {
            event: 'connection.update',
            func: async ({ qr, connection, lastDisconnect }) => {
                if (qr) {
                    this.emit('require_action', {
                        instructions: [
                            `Debes escanear el QR Code para iniciar session reivsa qr.png`,
                            `Recuerda que el QR se actualiza cada minuto `,
                            `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`,
                        ],
                    })
                    await baileyGenerateImage(qr)
                }

                if (lastDisconnect?.error) {
                    this.emit('require_action', {
                        instructions: [
                            `Algo sucedio reinicia el bot o revisa tu whatsapp`,
                            `Necesitas ayuda: https://link.codigoencasa.com/DISCORD`,
                        ],
                    })
                }

                if (connection === 'open') this.emit('ready', true)
            },
        },
        {
            event: 'messages.upsert',
            func: ({ messages }) => {
                const [messageCtx] = messages
                let payload = {
                    ...messageCtx,
                    body: messageCtx?.message?.conversation,
                    from: messageCtx?.key?.remoteJid,
                }
                if (payload.from === 'status@broadcast') {
                    return
                }

                if (!baileyIsValidNumber(payload.from)) {
                    return
                }
                payload.from = baileyCleanNumber(payload.from, true)
                this.emit('message', payload)
            },
        },
    ]

    initBusEvents = () => {
        const listEvents = this.busEvents()

        for (const { event, func } of listEvents) {
            this.vendor.ev.on(event, func)
        }
    }

    /**
     * @alpha
     * @param {string} number
     * @param {string} message
     * @example await sendMessage('+XXXXXXXXXXX', 'https://dominio.com/imagen.jpg' | 'img/imagen.jpg')
     */

    sendMedia = async (number, imageUrl) => {
        await this.vendor.sendMessage(number, {
            image: { url: imageUrl },
        })
    }

    /**
     * @alpha
     * @param {string} number
     * @param {string} message
     * @param {boolean} voiceNote optional
     * @example await sendMessage('+XXXXXXXXXXX', 'audio.mp3')
     */

    sendAudio = async (number, audioUrl, voiceNote = false) => {
        const numberClean = number.replace('+', '')
        await this.vendor.sendMessage(`${numberClean}@c.us`, {
            audio: { url: audioUrl },
            ptt: voiceNote,
        })
    }

    /**
     *
     * @param {string} number
     * @param {string} message
     * @returns
     */
    sendText = async (number, message) => {
        return this.vendor.sendMessage(number, { text: message })
    }
    /**
     * TODO: Necesita terminar de implementar el sendMedia y sendButton guiarse:
     * https://github.com/leifermendez/bot-whatsapp/blob/4e0fcbd8347f8a430adb43351b5415098a5d10df/packages/provider/src/web-whatsapp/index.js#L165
     * @param {string} number
     * @param {string} message
     * @example await sendMessage('+XXXXXXXXXXX', 'Hello World')
     */
    sendMessage = async (numberIn, message, { options }) => {
        const number = baileyCleanNumber(numberIn)

        // if (options?.buttons?.length)
        //     return this.sendButtons(number, message, options.buttons)
        if (options?.media) return this.sendMedia(number, options.media)
        return this.sendText(number, message)
    }

    /**
     *
     * @param {string} number
     * @param {string} filePath
     * @example await sendMessage('+XXXXXXXXXXX', './document/file.pdf')
     */

    sendFile = async (number, filePath) => {
        if (fs.existsSync(filePath)) {
            const mimeType = mime.lookup(filePath)
            const numberClean = number.replace('+', '')
            const fileName = filePath.split('/').pop()

            await this.vendor.sendMessage(`${numberClean}@c.us`, {
                document: { url: filePath },
                mimetype: mimeType,
                fileName: fileName,
            })
        }
    }

    /**
     *
     * @param {string} number
     * @param {string} text
     * @param {string} footer
     * @param {Array} buttons
     * @example await sendMessage("+XXXXXXXXXXX", "Your Text", "Your Footer", [{"buttonId": "id", "buttonText": {"displayText": "Button"}, "type": 1}])
     */

    sendButtons = async (number, text, footer, buttons) => {
        const numberClean = number.replace('+', '')

        const buttonMessage = {
            text: text,
            footer: footer,
            buttons: buttons,
            headerType: 1,
        }

        await this.vendor.sendMessage(`${numberClean}@c.us`, buttonMessage)
    }
}

module.exports = BaileysProvider
