import {PythonShell} from 'python-shell';
import path from 'path';
import fs from 'fs';
import net from 'net';
import {promisify} from 'util';
import {exec} from 'child_process';
import {fileURLToPath} from "url";

const execAsync = promisify(exec);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.join(__dirname, '../');
const hasWriteAccess = !process.env.VERCEL; // 非vercel环境才有write权限

function ensureDir(dir) {
    if (hasWriteAccess) {
        if (!fs.existsSync(dir)) {
            fs.mkdirSync(dir, {recursive: true});
        }
    }
}

function log(logFile, level, msg) {
    const line = `${new Date().toISOString()} [${level}] ${msg}\n`;
    fs.appendFileSync(logFile, line);
    if (level === 'ERROR' || level === 'CRITICAL') {
        console.error(line.trim());
    } else {
        console.log(line.trim());
    }
}

export class DaemonManager {
    constructor(rootDir, daemonMode = 0) {
        this.rootDir = rootDir;
        this.daemonShell = null;
        this.daemonFile = daemonMode ? 't4_daemon_lite.py' : 't4_daemon.py'
        this.config = this.getDaemonConfig();
    }

    getDaemonConfig() {
        const logsDir = path.join(this.rootDir, 'logs');
        ensureDir(logsDir);

        return {
            pidFile: path.join(this.rootDir, 't4_daemon.pid'),
            logFile: path.join(logsDir, 'daemon.log'),
            daemonScript: path.join(this.rootDir, 'spider/py/core', this.daemonFile),
            clientScript: path.join(this.rootDir, 'spider/py/core', 'bridge.py'),
            host: '127.0.0.1',
            port: 57570,
        };
    }

    getPythonPath() {
        if (process.env.PYTHON_PATH) return process.env.PYTHON_PATH;
        if (process.env.VIRTUAL_ENV) {
            return process.platform === 'win32'
                ? path.join(process.env.VIRTUAL_ENV, 'Scripts', 'python')
                : path.join(process.env.VIRTUAL_ENV, 'bin', 'python');
        }
        return process.platform === 'win32' ? 'python.exe' : 'python3';
    }

    async isPythonAvailable() {
        try {
            const {stdout} = await execAsync(`${this.getPythonPath()} --version`);
            return stdout.includes('Python');
        } catch {
            return false;
        }
    }

    cleanupFiles() {
        if (hasWriteAccess) {
            try {
                if (fs.existsSync(this.config.pidFile)) fs.unlinkSync(this.config.pidFile);
            } catch {
            }
        }
    }

    isDaemonRunning() {
        if (!fs.existsSync(this.config.pidFile)) return false;
        const pid = parseInt(fs.readFileSync(this.config.pidFile, 'utf8'), 10);
        try {
            process.kill(pid, 0);
            return true;
        } catch {
            return false;
        }
    }

    async waitForServer(timeoutMs = 5000) {
        const {host, port} = this.config;
        const deadline = Date.now() + timeoutMs;

        return new Promise((resolve, reject) => {
            const tryConnect = () => {
                const socket = net.connect({host, port}, () => {
                    socket.end();
                    resolve(true);
                });
                socket.on('error', () => {
                    if (Date.now() > deadline) {
                        reject(new Error('守护进程未能在超时时间内启动'));
                    } else {
                        setTimeout(tryConnect, 300);
                    }
                });
            };
            tryConnect();
        });
    }

    async startDaemon() {
        if (this.isDaemonRunning()) {
            log(this.config.logFile, 'INFO', 'Python 守护进程已在运行');
            return;
        }
        if (!await this.isPythonAvailable()) {
            log(this.config.logFile, 'INFO', '当前环境不支持Python,跳过启动守护进程');
            return;
        }

        this.cleanupFiles();

        const options = {
            mode: 'text',
            pythonPath: this.getPythonPath(),
            pythonOptions: ['-u'],
            scriptPath: path.dirname(this.config.daemonScript),
            env: {PYTHONIOENCODING: 'utf-8'},
            args: [
                '--pid-file', this.config.pidFile,
                '--log-file', this.config.logFile,
                '--host', this.config.host,
                '--port', this.config.port,
            ],
        };

        log(this.config.logFile, 'INFO', `正在启动 Python 守护进程 [${this.daemonFile}]...`);
        const daemonShell = new PythonShell(path.basename(this.config.daemonScript), options);
        this.daemonShell = daemonShell;

        daemonShell.on('message', (m) => log(this.config.logFile, 'INFO', `[守护进程] ${m}`));
        daemonShell.on('stderr', (m) => log(this.config.logFile, 'INFO', `[守护进程] ${m}`));
        daemonShell.on('error', (err) => log(this.config.logFile, 'CRITICAL', `错误: ${err.message}`));
        daemonShell.on('close', (code, signal) => {
            if (code !== null && code !== undefined) {
                log(this.config.logFile, 'INFO', `[ON CLOSE]守护进程关闭，退出码: ${code}`);
            } else {
                log(this.config.logFile, 'WARN', `守护进程异常退出（可能被 kill），未返回退出码`);
            }
            this.cleanupFiles();
            this.daemonShell = null;
        });


        daemonShell.childProcess.on('spawn', () => {
            if (hasWriteAccess) {
                fs.writeFileSync(this.config.pidFile, daemonShell.childProcess.pid.toString());
            }
            log(this.config.logFile, 'INFO', `守护进程启动成功，PID: ${daemonShell.childProcess.pid}`);
        });

        await this.waitForServer();
    }

    async stopDaemon() {
        if (!this.isDaemonRunning()) {
            log(this.config.logFile, 'INFO', '没有运行的守护进程');
            return;
        }

        log(this.config.logFile, 'INFO', '正在停止守护进程...');
        const pid = parseInt(fs.readFileSync(this.config.pidFile, 'utf8'), 10);

        if (process.platform === 'win32') {
            exec(`taskkill /PID ${pid} /T /F`);
        } else {
            try {
                process.kill(pid, 'SIGTERM');
            } catch {
            }
        }

        await new Promise((resolve) => setTimeout(resolve, 3000));

        if (this.isDaemonRunning()) {
            log(this.config.logFile, 'WARN', '守护进程未退出，强制终止...');
            try {
                process.kill(pid, 'SIGKILL');
            } catch {
            }
        }

        this.cleanupFiles();
        this.daemonShell = null;
        log(this.config.logFile, 'INFO', '守护进程已停止');
    }
}


export const daemon = new DaemonManager(rootDir, Number(process.env.daemonMode) || 0);