require("../lib/logger");
const { spawn } = require("child_process");
const { resolve } = require("path");
const fs = require("fs");

const status = {};
let gpioProcess;
let gpioStopping = false;
let gpioRestartTimer;
const listenCallbacks = new Map();
let encoderCallback;
let encoderStartTimer;
let encoderLastConfig;

function cleanupStalePigpio() {
  const pidFile = "/var/run/pigpio.pid";
  if (!fs.existsSync(pidFile)) return;
  let pid;
  try {
    pid = parseInt(fs.readFileSync(pidFile, "utf8"), 10);
  } catch (e) {
    return;
  }
  if (!pid || Number.isNaN(pid)) return;
  try {
    const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, "utf8");
    if (cmdline.includes("/network-rc/lib/gpio.js")) {
      try {
        process.kill(pid, "SIGTERM");
      } catch (e) {}
    } else {
      return;
    }
  } catch (e) {
    // 进程不存在或无法读取 cmdline
  }
  try {
    fs.unlinkSync(pidFile);
  } catch (e) {}
}

function startGpioProcess() {
  if (gpioRestartTimer) {
    clearTimeout(gpioRestartTimer);
    gpioRestartTimer = null;
  }
  gpioStopping = false;
  cleanupStalePigpio();
  gpioProcess = spawn("sudo", [process.execPath, resolve(__dirname, "./gpio.js")]);
  logger.info("GPIO 子进程启动中...");

  if (encoderLastConfig) {
    if (encoderStartTimer) {
      clearTimeout(encoderStartTimer);
      encoderStartTimer = null;
    }
    encoderStartTimer = setTimeout(() => {
      encoderStartTimer = null;
      exports.startEncoder(encoderLastConfig, encoderCallback);
    }, 800);
  }

  if (gpioProcess.stdin) {
    gpioProcess.stdin.on("error", (error) => {
      if (error && error.code === "EPIPE") {
        logger.warn("GPIO stdin 已关闭");
        return;
      }
      logger.error(`[GPIO Server Stdin Error] ${error.message}`);
    });
  }

  gpioProcess.stdout.on("data", (data) => {
    const lines = data
      .toString()
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean);

    lines.forEach((line) => {
      if (line.startsWith("gpio-change|")) {
        const [, json] = line.split("|");
        if (!json) return;
        try {
          const { pin, value } = JSON.parse(json);
          const callback = listenCallbacks.get(pin);
          if (callback) callback(value);
        } catch (error) {
          logger.error(`[GPIO Server Out Parse Error] ${error.message}`);
        }
        return;
      }

      if (line.startsWith("encoder|")) {
        const [, json] = line.split("|");
        if (!json) return;
        try {
          const payload = JSON.parse(json);
          if (encoderCallback) encoderCallback(payload);
        } catch (error) {
          logger.error(`[Encoder Out Parse Error] ${error.message}`);
        }
        return;
      }

      if (process.env.NRC_GPIO_VERBOSE === "1") {
        logger.info(`[GPIO Server] ${line}`);
        return;
      }
      if (/\bwarn\b|\berror\b/i.test(line)) {
        logger.warn(`[GPIO Server] ${line}`);
      }
    });
  });

  gpioProcess.stderr.on("data", (data) => {
    logger.error(`[GPIO Server Error] ${data}`);
  });

  gpioProcess.on("error", (error) => {
    logger.error(`[GPIO Server Spawn Error] ${error.message}`);
    gpioProcess = null;
    if (!gpioStopping) {
      gpioRestartTimer = setTimeout(startGpioProcess, 1000);
    }
  });

  gpioProcess.on("exit", (code, signal) => {
    logger.error(`[GPIO Server Exit] code=${code} signal=${signal}`);
    gpioProcess = null;
    if (!gpioStopping) {
      gpioRestartTimer = setTimeout(startGpioProcess, 1000);
    }
  });
}

function ensureGpioProcess() {
  if (
    !gpioProcess ||
    gpioProcess.killed ||
    !gpioProcess.stdin ||
    !gpioProcess.stdin.writable
  ) {
    startGpioProcess();
  }
  return !!(gpioProcess && gpioProcess.stdin && gpioProcess.stdin.writable);
}

startGpioProcess();

exports.listen = function (pin, callback) {
  listenCallbacks.set(pin, callback);
  if (!ensureGpioProcess()) {
    logger.error("GPIO 进程不可用，无法监听引脚");
    return;
  }
  gpioProcess.stdin.write(`listen ${pin}\n`);
};

exports.startEncoder = function (config = {}, callback) {
  encoderLastConfig = config;
  encoderCallback = typeof callback === "function" ? callback : null;

  if (!ensureGpioProcess()) {
    if (!encoderStartTimer) {
      logger.warn("GPIO 进程未就绪，稍后重试启动编码器");
      encoderStartTimer = setTimeout(() => {
        encoderStartTimer = null;
        exports.startEncoder(encoderLastConfig || {}, encoderCallback);
      }, 500);
    }
    return;
  }

  if (encoderStartTimer) {
    clearTimeout(encoderStartTimer);
    encoderStartTimer = null;
  }

  try {
    gpioProcess.stdin.write(`encoder ${JSON.stringify(config)}\n`);
  } catch (error) {
    if (error && error.code !== "EPIPE") {
      logger.error(`Encoder 启动失败: ${error.message}`);
    }
  }
};

exports.stopEncoder = function () {
  encoderCallback = null;
  if (!gpioProcess || !gpioProcess.stdin || !gpioProcess.stdin.writable) return;
  try {
    gpioProcess.stdin.write(`encoder-stop\n`);
  } catch (error) {
    if (error && error.code !== "EPIPE") {
      logger.error(`Encoder 停止失败: ${error.message}`);
    }
  }
};

exports.changePwmPin = function (pin, v) {
  if (!ensureGpioProcess()) {
    logger.error("GPIO 进程不可用，无法写入 PWM");
    return;
  }
  gpioProcess.stdin.write(`pwm ${pin} ${v}\n`);
};

exports.changeSwitchPin = function (pin, v) {
  if (!ensureGpioProcess()) {
    logger.error("GPIO 进程不可用，无法写入开关");
    return;
  }
  gpioProcess.stdin.write(`sw ${pin} ${v}\n`);
};

exports.channelStatus = status;

exports.closeChannel = function () {
  if (!gpioProcess || !gpioProcess.stdin || !gpioProcess.stdin.writable) return;
  try {
    gpioProcess.stdin.write(`close\n`);
  } catch (error) {
    if (error && error.code !== "EPIPE") {
      logger.error(`GPIO close 失败: ${error.message}`);
    }
  }
};

function shutdownGpioProcess(timeout = 1500) {
  gpioStopping = true;
  if (gpioRestartTimer) {
    clearTimeout(gpioRestartTimer);
    gpioRestartTimer = null;
  }
  return new Promise((resolve) => {
    if (!gpioProcess) return resolve();
    const proc = gpioProcess;
    let done = false;
    const finish = () => {
      if (done) return;
      done = true;
      if (gpioProcess === proc) gpioProcess = null;
      resolve();
    };

    proc.once("exit", finish);
    try {
      if (proc.stdin && proc.stdin.writable) {
        proc.stdin.write(`exit\n`);
      }
    } catch (e) {}

    setTimeout(() => {
      try {
        proc.kill("SIGTERM");
      } catch (e) {}
      setTimeout(() => {
        try {
          proc.kill("SIGKILL");
        } catch (e) {}
        finish();
      }, 300);
    }, timeout);
  });
}

exports.shutdownGpioProcess = shutdownGpioProcess;

process.on("exit", () => {
  if (gpioProcess && gpioProcess.stdin && gpioProcess.stdin.writable) {
    gpioProcess.stdin.write(`exit\n`);
  }
});
