import { Router, Request, Response, NextFunction } from "express";
import { env } from "../config/env";
import { createDeviceHeartbeat } from "../shared/store/heartbeatStore";
import { getDeviceBySerialNumber, patchDevice } from "../shared/store/deviceStore";

const router = Router();

type Qf100ConfigRequest = {
  "dev-model"?: string;
  "item-number"?: string;
  "dev-sn"?: string;
  "hardware-config"?: string;
  "fw-version"?: string;
  "fw-build"?: number;
  "app-config-version"?: number;
  imei?: string;
  imsi?: string;
  iccid?: string;
};

type SpeakerReportRequest = Qf100ConfigRequest & {
  result?: number;
  "success-list"?: string[];
  "fail-list"?: string[];
  "wifi-ap"?: Record<string, unknown>;
  "wifi-ap-list"?: Record<string, unknown>[];
  "main-cell-info"?: Record<string, unknown>;
  "neighbor-cell-list"?: Record<string, unknown>[];
};

function parseBrokerUrl() {
  if (!env.MQTT_BROKER_URL) {
    return { host: "", port: 0 };
  }

  try {
    const parsed = new URL(env.MQTT_BROKER_URL);
    return {
      host: parsed.hostname,
      port: parsed.port ? Number(parsed.port) : parsed.protocol === "mqtts:" ? 8883 : 1883
    };
  } catch (_error) {
    return { host: "", port: 0 };
  }
}

function getRequestPayload(req: Request): Qf100ConfigRequest {
  return {
    ...((req.query || {}) as Qf100ConfigRequest),
    ...((req.body || {}) as Qf100ConfigRequest)
  };
}

function vendorError(res: Response, code: number, message: string) {
  res.status(200).json({ "error-code": code });
}

function toDeviceDateTime(date: Date) {
  const pad = (value: number) => String(value).padStart(2, "0");
  return [
    date.getUTCFullYear(),
    pad(date.getUTCMonth() + 1),
    pad(date.getUTCDate()),
    pad(date.getUTCHours()),
    pad(date.getUTCMinutes()),
    pad(date.getUTCSeconds())
  ].join("");
}

function numberFromPayload(value: unknown, fallback: number) {
  const parsed = Number(value);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
}

function getAppConfigVersion(payload: Qf100ConfigRequest) {
  const requested = numberFromPayload(payload["app-config-version"], 1);
  return Math.max(env.QF100_APP_CONFIG_VERSION || requested, requested);
}

function buildDefaultAudioConfig() {
  return {
    "voice-type": 2,
    "voice-volume": 80,
    "voice-format": 1,
    "dialect-id": 0
  };
}

function buildOptionalDynamicQrConfig(device: { capability_profile_json?: Record<string, unknown> }) {
  const profile = device.capability_profile_json || {};
  const url = String(profile.dynamic_qr_url || profile.dynamicQrUrl || env.QF100_DYNAMIC_QR_URL || "").trim();
  if (!url) {
    return null;
  }

  return {
    url,
    gap: numberFromPayload(profile.dynamic_qr_gap || profile.dynamicQrGap, env.QF100_DYNAMIC_QR_GAP_SECONDS)
  };
}

async function handleDevConfig(req: Request, res: Response, next: NextFunction) {
  try {
    const payload = getRequestPayload(req);
    const serialNumber = String(payload["dev-sn"] || "").trim();

    if (!serialNumber) {
      return vendorError(res, 1001, "dev-sn is required");
    }

    const device = await getDeviceBySerialNumber(serialNumber);
    if (!device || device.status !== "active") {
      return vendorError(res, 1002, "device not registered or inactive");
    }

    const now = new Date();
    const firmwareVersion = payload["fw-version"] ? String(payload["fw-version"]) : device.firmware_version;
    await patchDevice(device.id, {
      vendor: device.vendor || "QF",
      model: payload["dev-model"] ? String(payload["dev-model"]) : device.model || "QF100",
      communication_mode: "mqtt",
      last_seen_at: now.toISOString(),
      firmware_version: firmwareVersion
    });

    await createDeviceHeartbeat({
      device_id: device.id,
      timestamp: now.toISOString(),
      firmware_version: firmwareVersion,
      state: "config_pull",
      payload_json: {
        source: "qf100_dev_config",
        dev_model: payload["dev-model"],
        item_number: payload["item-number"],
        dev_sn: serialNumber,
        hardware_config: payload["hardware-config"],
        fw_build: payload["fw-build"],
        app_config_version: payload["app-config-version"],
        imei: payload.imei,
        imsi: payload.imsi,
        iccid: payload.iccid
      }
    });

    const brokerFromUrl = parseBrokerUrl();
    const brokerHost = env.QF100_MQTT_BROKER_HOST || brokerFromUrl.host;
    const brokerPort = env.QF100_MQTT_BROKER_PORT || brokerFromUrl.port;
    const username = env.QF100_MQTT_USERNAME || device.mqtt_username || device.id;
    const password = env.QF100_MQTT_PASSWORD || env.MQTT_PASSWORD;

    if (!brokerHost || !brokerPort) {
      return vendorError(res, 1003, "mqtt broker is not configured");
    }

    const dynamicQrConfig = buildOptionalDynamicQrConfig(device);
    res.json({
      "error-code": 0,
      "bind-state": 1,
      "app-config-version": getAppConfigVersion(payload),
      "time-stamp": Math.floor(now.getTime() / 1000),
      "date-time": toDeviceDateTime(now),
      mqtt: {
        "broker-ip": brokerHost,
        "broker-port": brokerPort,
        "client-id": `soundbox-${serialNumber}`,
        "user-name": username,
        password,
        "subscribe-topic": `soundbox/${serialNumber}/down`,
        "publish-topic": `soundbox/${serialNumber}/up`,
        "keep-alive": env.QF100_MQTT_KEEP_ALIVE_SECONDS,
        "cert-update": 0
      },
      audio: buildDefaultAudioConfig(),
      "trade-audio-templates": [],
      "discount-audio-templates": [],
      "verify-code-audio-templates": [],
      "power-on-audio": {
        "play-mode": 1,
        "voice-format": 1,
        name: "",
        data: ""
      },
      "power-off-audio": {
        "play-mode": 1,
        "voice-format": 1,
        name: "",
        data: ""
      },
      "ad-audio": {
        "play-mode": 0,
        "time-period": {
          start: 8,
          end: 22
        },
        interval: 0,
        "pay-count": 0,
        "voice-format": 1,
        name: "",
        data: ""
      },
      "customer-audio": {
        "play-mode": 0,
        "voice-format": 1,
        name: "",
        data: ""
      },
      ...(dynamicQrConfig ? { "dynamic-qr": dynamicQrConfig } : {})
    });
  } catch (error) {
    next(error);
  }
}

async function recordSpeakerReport(req: Request, state: string) {
  const payload = getRequestPayload(req) as SpeakerReportRequest;
  const serialNumber = String(payload["dev-sn"] || "").trim();
  if (!serialNumber) {
    return null;
  }

  const device = await getDeviceBySerialNumber(serialNumber);
  if (!device) {
    return null;
  }

  const now = new Date();
  await patchDevice(device.id, {
    last_seen_at: now.toISOString()
  });
  await createDeviceHeartbeat({
    device_id: device.id,
    timestamp: now.toISOString(),
    firmware_version: payload["fw-version"] ? String(payload["fw-version"]) : device.firmware_version,
    state,
    payload_json: {
      source: `speaker_${state}`,
      ...payload
    }
  });

  return device;
}

async function handleAckReport(req: Request, res: Response, next: NextFunction) {
  try {
    await recordSpeakerReport(req, req.path.replace(/^\//, "").replace(/-/g, "_"));
    res.json({ "error-code": 0 });
  } catch (error) {
    next(error);
  }
}

async function handleCheckUpdate(req: Request, res: Response, next: NextFunction) {
  try {
    const payload = getRequestPayload(req) as SpeakerReportRequest;
    await recordSpeakerReport(req, "check_update");
    res.json({
      "error-code": 1002,
      version: payload["fw-version"] || "",
      build: Number(payload["fw-build"] || 0),
      "file-length": 0,
      "file-hash": "",
      "download-url": ""
    });
  } catch (error) {
    next(error);
  }
}

router.get("/dev-config", handleDevConfig);
router.post("/dev-config", handleDevConfig);
router.post("/lbs-info", handleAckReport);
router.post("/config-update-result", handleAckReport);
router.get("/check-update", handleCheckUpdate);
router.post("/check-update", handleCheckUpdate);
router.post("/fw-update-result", handleAckReport);

export default router;
