const net = require('net')
const CDP = require('chrome-remote-interface')
const chromeFinder = require('chrome-finder')

const {
    exec,
    spawn,
} = require('child_process')
const path = require('node:path')
const os = require('node:os')

class WebDriver {

    constructor (params = {
        args: [],
        host: '127.0.0.1',
        port: 0,
        chromePath: '',
        userDataDir: '',
    }) {
        this.args = params.args || []
        this.host = params.host || '127.0.0.1'
        this.port = params.port || 0
        this.chromePath = params.chromePath || ''
        this.userDataDir = params.userDataDir || ''
    }

    headless () {
        this.args.push('--headless=old')
    }

    getCDP () {
        return this.cdp
    }

    setProxy (proxy) {
        this.args.push(`--proxy-server=${proxy}`)
    }

    addArguments (arg) {
        if (arg) {
            this.args.push(arg)
        } else {
            throw new Error('Arguments cannot be null')
        }
    }

    setChromeBinaryPath (binaryPath) {
        this.chromePath = binaryPath
    }

    setUserDataDirPath (binaryPath) {
        this.userDataDir = binaryPath
    }

    async builder () {
        try {
            if (!this.port) {
                this.port = await this.isPortTaken(this.getRandomNumber())
            }
            if (!this.chromePath) {
                this.chromePath = chromeFinder()
            }

            if (!this.userDataDir) {
                this.userDataDir = path.join(os.tmpdir(), `scoped_dir${new Date().getTime()}`)
            }

            console.info('启动端口：', this.port)

            const command = `${this.chromePath}`
            const args = [
                `--user-data-dir=${this.userDataDir}`,
                `--disk-cache-dir=${this.userDataDir}`,
                `--remote-debugging-port=${this.port}`,
                '--no-first-run',
                '--no-dleck',
                '--disable-plugins',
                '--no-default-browser-check',
                '--new-tab',
                // 'data:,',
            ]
            const mergedArray = args.concat(this.args)

            const { spawn } = require('child_process')

            return new Promise((resolve, reject) => {
                const childProcess = spawn(command, mergedArray)

                childProcess.stderr.on('data', async (data) => {
                    await this.connectToChrome(1, this.port)
                    resolve(true)
                })

                childProcess.on('close', (code) => {
                    console.info(`子进程退出成功，退出码: ${code}`)
                })

                childProcess.on('error', (err) => {
                    console.error(`子进程错误：${err.message}`)
                })
            })
        } catch (e) {
            console.error(e)
        }
    }

    createCDPConnection () {
        return {
            host: this.host,
            port: this.port,
        }
    }

    checkBrowser () {
        return !!(this.chromePath || chromeFinder())
    }

    isPortTaken (port) {
        return new Promise((resolve) => {
            const server = net.createServer()

            server.on('listening', () => {
                server.close(() => {
                    resolve(port)
                })
            })

            server.on('error', async () => {
                this.isPortTaken(port + 1).then(resolve)
            })

            server.listen(port, '127.0.0.1')
        })
    }

    async connectToChrome (time, port) {
        const waitingTime = {
            1: 1000,
            2: 3000,
            3: 5000,
            4: 10000,
        }

        if (!this.cdp) {
            try {
                this.cdp = await CDP({ port })
                console.info('CDP 连接成功')
            } catch (error) {
                console.error('连接失败，正在重试...')
                await new Promise(resolve => setTimeout(resolve, waitingTime[time]))

                if (time < 4) {
                    await this.connectToChrome(time + 1, port)
                } else {
                    await this.quit()
                    await this.builder()
                }
            }
        }
    }

    getRandomNumber () {
        return Math.floor(Math.random() * (65535 - 10000 + 1)) + 10000
    }

    quit () {
        const command = `netstat -ano | findstr LISTENING | findstr :${this.port}`

        return new Promise((resolve, reject) => {
            exec(command, (error, stdout, stderr) => {
                if (error) {
                    resolve(`执行命令时出错: ${error}`)
                    return
                }

                const lines = stdout.split('\n')

                const processIds = []

                lines.forEach((line) => {
                    const parts = line.trim().split(/\s+/)

                    if (parts.length >= 2) {
                        const pid = parts[parts.length - 1]
                        processIds.push(pid)
                    }
                })

                if (processIds.length === 0) {
                    resolve(`未找到在端口 ${this.port} 上运行的进程`)
                    return
                }

                if (processIds.length > 0) {
                    processIds.forEach((pid) => {
                        exec(`taskkill /F /PID ${pid}`, (error) => {
                            if (error) {
                                resolve(`关闭进程时出错: ${error}`)
                            } else {
                                resolve(this.port)
                            }
                        })
                    })
                }
            })
        })
    }
}

module.exports = { WebDriver: WebDriver }
