const { Server: SocketIO } = require('socket.io')
const Server = require('./server')
const path = require('path')
const fs = require('fs')
const AI = require('./ai')
const axios = require('axios')
const FormData = require('form-data')

module.exports = class Socket {
    static io = null

    static init() {
        this.io = new SocketIO(Server.server, {
            cors: {
                origin: '*',
                methods: ['GET', 'POST'],
            },
        })

        this.io.on('connection', (socket) => {
            // headers
            const origin = socket?.handshake?.headers?.origin || null
            const allowedOrigins = ['http://127.0.0.1:8000', 'http://localhost:8000']

            // console.log(origin, allowedOrigins)

            // helpers
            const getHost = (url) => {
                try {
                    return new URL(url).host
                } catch {
                    return null
                }
            }

            // checkers
            const originHost = origin ? getHost(origin) : null
            const allowedHosts = allowedOrigins.map(getHost)
            // console.log(originHost, allowedHosts)
            if (!originHost || !allowedHosts.includes(originHost)) {
                // console.log(`[SOCKET] Client ${socket.id} rejected, origin: ${origin}`)
                socket.disconnect(true)
                return
            }

            // listeners
            // // console.log('[SOCKET] Client connected:', socket.id, 'origin:', origin)

            socket.on('upload', async (data) => {
                try {
                    // console.log('upload')
                    const { fileName, buffer } = data
                    const ext = path.extname(fileName)
                    const baseName = path.basename(fileName, ext)
                    const uniqid = Date.now() + '-' + Math.floor(Math.random() * 10000)
                    const newFileName = `${baseName}-${uniqid}${ext}`
                    const savePath = path.join(__dirname, '../uploads', newFileName)

                    // 🔹 Convert buffer to Node.js Buffer
                    let fileBuffer
                    if (buffer instanceof ArrayBuffer) {
                        fileBuffer = Buffer.from(new Uint8Array(buffer))
                    } else if (Array.isArray(buffer)) {
                        fileBuffer = Buffer.from(buffer)
                    } else {
                        fileBuffer = Buffer.from(buffer)
                    }

                    // 🔹 Save PDF file
                    await fs.promises.writeFile(savePath, fileBuffer)
                    // console.log('[UPLOAD SUCCESS]', savePath)

                    socket.emit('upload_progress', {
                        step: 'file_saved',
                        message: 'PDF file has been saved',
                    })
                    this.delay(1500)

                    // 🔹 Convert PDF to HTML
                    const htmlFile = await AI.pdfToHtml(savePath)
                    // console.log('✅ Conversion finished, HTML file located at:', htmlFile)

                    // 🔹 Check if HTML file exists in uploads/html
                    const htmlDir = path.join(__dirname, '../uploads/html')
                    const htmlBaseName = path.basename(htmlFile)
                    const finalHtmlPath = path.join(htmlDir, htmlBaseName)

                    try {
                        await fs.promises.access(finalHtmlPath, fs.constants.F_OK)
                        // console.log('✅ HTML file found at:', finalHtmlPath)

                        socket.emit('upload_progress', {
                            step: 'converted',
                            message: 'PDF has been converted to HTML'
                        })
                        this.delay(1500)
                    } catch {
                        throw new Error(`❌ HTML file not found at ${finalHtmlPath}`)
                    }

                    // 🔹 Delete PDF after conversion
                    try {
                        await fs.promises.unlink(savePath)
                        // console.log('🗑️ PDF deleted:', savePath)

                        socket.emit('upload_progress', {
                            step: 'pdf_deleted',
                            message: 'PDF file deleted after conversion',
                        })
                        this.delay(1500)
                    } catch (e) {
                        // console.error('❌ Failed to delete PDF:', e.message)
                    }

                    // 🔹 Send to webhook
                    const form = new FormData()
                    form.append('file', fs.createReadStream(htmlFile))
                    form.append('original_pdf_name', newFileName)

                    const webhookUrl = 'http://127.0.0.1:8000/api/webhook/pdftohtml'
                    const response = await axios.post(webhookUrl, form, {
                        headers: form.getHeaders(),
                        maxContentLength: Infinity,
                        maxBodyLength: Infinity,
                    })

                    // console.log('✅ Webhook response:', response.data)

                    if (!response.data.status) {
                        socket.emit('upload_progress', {
                            step: 'webhook_sent',
                            message: 'HTML file failed to saved'
                        })
                    } else {
                        socket.emit('upload_progress', {
                            step: 'webhook_sent',
                            message: 'HTML file successfully saved'
                        })
                    }

                    // ✅ Consistent success response
                    const output = response.data

                    socket.emit('upload_status', output)
                    return output

                } catch (err) {
                    // console.error('❌ ERROR:', err.message)

                    // ❌ Consistent error response
                    const output = {
                        status: false,
                        code: 500,
                        message: err.message || 'An error occurred',
                        result: null
                    }

                    socket.emit('upload_status', output)
                    return output
                }
            })

            socket.on('disconnect', () => {
                // console.log('[SOCKET] Client disconnected:', socket.id)
            })
        })
    }

    static delay(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms))
    }
}
