import { createGuid } from '../utils/uuid.js';
import { kafkaPayloadSchema } from '../schema/kafkaPayload.js';
import projectMetadata from '../cache/projectMetadata.js';
import { config } from '../config/config.js';

const normalizeDirection = (value) => {
  if (!value) return null;
  if (value === '上报' || value === '上传') return '上报';
  if (value === '下发') return '下发';
  return value;
};

const normalizeCmdWord = (value) => {
  if (typeof value === 'string') {
    const trimmed = value.trim();
    if (trimmed.startsWith('0x') || trimmed.startsWith('0X')) {
      return `0x${trimmed.slice(2).toLowerCase()}`;
    }
    if (/^[0-9a-fA-F]{2}$/.test(trimmed)) {
      return `0x${trimmed.toLowerCase()}`;
    }
    const parsed = Number(trimmed);
    if (Number.isFinite(parsed)) {
      return `0x${parsed.toString(16).toLowerCase()}`;
    }
    return trimmed;
  }
  if (typeof value === 'number' && Number.isFinite(value)) {
    return `0x${value.toString(16).toLowerCase()}`;
  }
  return null;
};

const resolveMessageType = (direction, cmdWord) => {
  if (cmdWord === '0x36' || cmdWord === '0x0e') {
    return '36上报';
  }
  if (cmdWord === '0x0f' && direction === '下发') {
    return '0F下发';
  }
  if (cmdWord === '0x0f' && direction === '上报') {
    return '0FACK';
  }
  return null;
};

const defaultDevTypeActionMap = {
  0: { name: '无效设备（也可以被认为是场景）', action: '无效' },
  1: { name: '强电继电器（输出状态）', action: '设备回路状态' },
  2: { name: '弱电输入（输入状态）', action: '用户操作' },
  3: { name: '弱电输出（输出状态）', action: '设备回路状态' },
  4: { name: '服务信息', action: '设备回路状态' },
  5: { name: '干节点窗帘', action: '设备回路状态' },
  6: { name: '开关', action: '用户操作' },
  7: { name: '空调', action: '用户操作' },
  8: { name: '红外感应', action: '用户操作' },
  9: { name: '空气质量检测设备', action: '设备回路状态' },
  10: { name: '插卡取电', action: '用户操作' },
  11: { name: '地暖', action: '用户操作' },
  12: { name: 'RCU 设备网络 - 没使用', action: '' },
  13: { name: '窗帘', action: '设备回路状态' },
  14: { name: '继电器', action: '设备回路状态' },
  15: { name: '红外发送', action: '设备回路状态' },
  16: { name: '调光驱动', action: '设备回路状态' },
  17: { name: '可控硅调光（可控硅状态）', action: '设备回路状态' },
  18: { name: '灯带（灯带状态） --2025-11-24 取消', action: '无效' },
  19: { name: '中控', action: '无效' },
  20: { name: '微信锁 （福 瑞狗的蓝牙锁 默认 0 地址）', action: '无效' },
  21: { name: '背景音乐（背景音乐状态）', action: '设备回路状态' },
  22: { name: '房态下发', action: '无效' },
  23: { name: '主机本地 调光', action: '无效' },
  24: { name: '485PWM 调光（ PWM 调光状态）', action: '无效' },
  25: { name: '总线调光（ PBLED 调光状态） - 没使用 -', action: '无效' },
  26: { name: 'RCU 电源', action: '无效' },
  27: { name: 'A9IO 开关', action: '用户操作' },
  28: { name: 'A9IO 扩展', action: '设备回路状态' },
  29: { name: 'A9IO 电源', action: '设备回路状态' },
  30: { name: '无线网关轮询（用于轮询控制轮询设备；给无线网关下发配置和询问网关状态）', action: '无效' },
  31: { name: '无线网关主动（用于主动控制主动设备）', action: '无效' },
  32: { name: '无线门磁', action: '用户操作' },
  33: { name: '空气参数显示设备', action: '设备回路状态' },
  34: { name: '无线继电器红外', action: '设备回路状态' },
  35: { name: '时间同步', action: '设备回路状态' },
  36: { name: '监控控制', action: '无效' },
  37: { name: '旋钮开关控制', action: '用户操作' },
  38: { name: 'CSIO - 类型', action: '设备回路状态' },
  39: { name: '插卡状态虚拟设备', action: '设备回路状态' },
  40: { name: '485 新风设备', action: '用户操作' },
  41: { name: '485 人脸机', action: '用户操作' },
  42: { name: '中控', action: '无效' },
  43: { name: '域控', action: '无效' },
  44: { name: 'LCD', action: '设备回路状态' },
  45: { name: '无卡断电 --2025-11-24 取消', action: '无效' },
  46: { name: '无卡取电 2', action: '用户操作' },
  47: { name: '虚拟时间设备', action: '设备回路状态' },
  48: { name: 'PLC 总控', action: '设备回路状态' },
  49: { name: 'PLC 设备 - 恒流调光设备', action: '设备回路状态' },
  50: { name: 'PLC 设备 - 恒压调光设备', action: '设备回路状态' },
  51: { name: 'PLC 设备 - 继电器设备', action: '设备回路状态' },
  52: { name: '色温调节功能', action: '设备回路状态' },
  53: { name: '蓝牙音频', action: '设备回路状态' },
  54: { name: '碳达人', action: '用户操作' },
  55: { name: '场景还原', action: '用户操作' },
  56: { name: '全局设置', action: '设备回路状态' },
  57: { name: '能耗检测', action: '设备回路状态' },
  241: { name: 'CSIO - 类型', action: '设备回路状态' }
};

// Parse env rules if present
let devTypeActionRules = [];
try {
  if (process.env.ACTION_TYPE_DEV_TYPE_RULES) {
    const parsed = JSON.parse(process.env.ACTION_TYPE_DEV_TYPE_RULES);
    if (Array.isArray(parsed)) {
      devTypeActionRules = parsed;
    }
  }
} catch (error) {
  // Silent fallback
}

const getActionTypeByDevType = (devType) => {
  // 1. Env override
  const rule = devTypeActionRules.find(r => r.dev_type === devType);
  if (rule?.action_type) return rule.action_type;

  // 2. Default map
  const entry = defaultDevTypeActionMap[devType];
  return entry?.action || '设备回路状态';
};

const getDevTypeName = (devType) => {
  // 1. Env override
  const rule = devTypeActionRules.find(r => r.dev_type === devType);
  if (rule?.name) return rule.name;

  // 2. Default map
  const entry = defaultDevTypeActionMap[devType];
  return entry?.name || 'Unknown';
};

const resolveDevTypeAction = (devType) => {
  if (devType === null || devType === undefined) return '设备回路状态';
  return getActionTypeByDevType(devType);
};

const parseKafkaPayload = (value) => {
  const raw = Buffer.isBuffer(value) ? value.toString('utf8') : value;
  if (typeof raw !== 'string') {
    throw new Error('Invalid kafka message value');
  }
  return JSON.parse(raw);
};

export const buildRowsFromMessageValue = (value) => {
  const payload = parseKafkaPayload(value);
  return buildRowsFromPayload(payload);
};

const isHexString = (value) => {
  if (typeof value !== 'string') {
    return false;
  }
  const trimmed = value.trim();
  if (!trimmed) {
    return false;
  }
  const compact = trimmed.replace(/[\s:]/g, '');
  if (compact.length === 0 || compact.length % 2 !== 0) {
    return false;
  }
  return /^[0-9a-fA-F]+$/.test(compact);
};

const hexToBase64 = (hex) => {
  const compact = hex.replace(/[\s:]/g, '');
  const buffer = Buffer.from(compact, 'hex');
  return buffer.toString('base64');
};

export const buildRowsFromPayload = (rawPayload) => {
  // 1. Validate and transform payload using Zod schema
  const payload = kafkaPayloadSchema.parse(rawPayload);

  const {
    ts_ms: tsMs,
    hotel_id: hotelId,
    room_id: roomId,
    device_id: deviceId,
    direction,
    cmd_word: cmdWord,
    frame_id: frameId,
    udp_raw: udpRawRaw,
    sys_lock_status: sysLockStatus,
    report_count: reportCount,
    fault_count: faultCount,
    device_list: deviceList, // Zod provides default []
    fault_list: faultList,   // Zod provides default []
    control_list: controlList, // Zod provides default []
    extra
  } = payload;

  // Temporary migration logic: if udp_raw is hex, convert to base64 before insert.
  // This is a transitional mechanism and should be removed once all upstream systems send base64 directly.
  const udpRaw = isHexString(udpRawRaw) ? hexToBase64(udpRawRaw) : udpRawRaw;

  const normalizedDirection = normalizeDirection(direction);
  const normalizedCmdWord = normalizeCmdWord(cmdWord);
  const messageType = resolveMessageType(normalizedDirection, normalizedCmdWord);
  const writeTsMs = Date.now();

  // Base fields common to all rows (excluding unique ID)
  // Check if hotel_id is within int2 range (-32768 to 32767), set to 0 if out of range
  const validHotelId = (hotelId >= -32768 && hotelId <= 32767) ? hotelId : 0;
  const commonFields = {
    ts_ms: tsMs,
    write_ts_ms: writeTsMs,
    hotel_id: validHotelId,
    room_id: roomId,
    device_id: deviceId,
    direction: normalizedDirection,
    cmd_word: normalizedCmdWord,
    frame_id: frameId,
    udp_raw: udpRaw,
    sys_lock_status: sysLockStatus ?? null,
    report_count: reportCount ?? null,
    fault_count: faultCount ?? null,
    // Initialize nullable fields
    dev_type: null,
    dev_addr: null,
    dev_loop: null,
    dev_data: null,
    error_type: null,
    error_data: null,
    type_l: null,
    type_h: null,
    details: null,
    extra: extra || {}
  };

  // Helper to generate loop name if not found in cache
  const getLoopNameWithFallback = (deviceId, devType, devAddr, devLoop) => {
    // 1. Try cache
    const cachedName = projectMetadata.getLoopName(deviceId, devType, devAddr, devLoop);
    if (cachedName) return cachedName;

    // 2. Check config for auto-generation
    if (!config.enableLoopNameAutoGeneration) {
      return null;
    }

    // 3. Fallback: [TypeName-Addr-Loop]
    const typeName = getDevTypeName(devType);
    if (!typeName) return null; // Should have a name if devType is valid

    return `[${devType}${typeName}-${devAddr}-${devLoop}]`;
  };

  const rows = [];

  // Logic 1: 0x36 Status/Fault Report
  if (messageType === '36上报') {
    const details = {
      device_list: deviceList,
      fault_list: faultList
    };

    // Process device status list
    if (deviceList.length > 0) {
      deviceList.forEach(device => {
        const actionType = resolveDevTypeAction(device.dev_type);
        rows.push({
          ...commonFields,
          guid: createGuid(),
          dev_type: device.dev_type ?? null,
          dev_addr: device.dev_addr ?? null,
          dev_loop: device.dev_loop ?? null,
          dev_data: device.dev_data ?? null,
          action_type: actionType,
          loop_name: getLoopNameWithFallback(deviceId, device.dev_type, device.dev_addr, device.dev_loop),
          details
        });
      });
    }

    // Process fault list
    if (faultList.length > 0) {
      faultList.forEach(fault => {
        const actionType = resolveDevTypeAction(fault.dev_type);
        rows.push({
          ...commonFields,
          guid: createGuid(),
          // Use common dev_ fields for fault device identification
          dev_type: fault.dev_type ?? null,
          dev_addr: fault.dev_addr ?? null,
          dev_loop: fault.dev_loop ?? null,
          error_type: fault.error_type ?? null,
          error_data: fault.error_data ?? null,
          action_type: actionType,
          loop_name: getLoopNameWithFallback(deviceId, fault.dev_type, fault.dev_addr, fault.dev_loop),
          details
        });
      });
    }

    // Fallback: if no lists, insert one record to preserve the event
    if (rows.length === 0) {
      rows.push({
        ...commonFields,
        guid: createGuid(),
        action_type: '设备回路状态',
        details
      });
    }

    return rows;
  }

  // Logic 2: 0x0F Control Command
  if (messageType === '0F下发') {
    const details = {
      control_list: controlList
    };

    if (controlList.length > 0) {
      controlList.forEach(control => {
        rows.push({
          ...commonFields,
          guid: createGuid(),
          dev_type: control.dev_type ?? null,
          dev_addr: control.dev_addr ?? null,
          dev_loop: control.dev_loop ?? null,
          type_l: control.type_l ?? null,
          type_h: control.type_h ?? null,
          action_type: '下发控制',
          loop_name: getLoopNameWithFallback(deviceId, control.dev_type, control.dev_addr, control.dev_loop),
          details
        });
      });
    }

    // Fallback
    if (rows.length === 0) {
      rows.push({
        ...commonFields,
        guid: createGuid(),
        action_type: '下发控制',
        details
      });
    }

    return rows;
  }

  // 3. 0x0F ACK
  else if (messageType === '0FACK') {
    const { control_list: controls = [] } = payload;
    if (Array.isArray(controls)) {
      const details = { control_list: controls };
      controls.forEach((control) => {
        rows.push({
          ...commonFields,
          guid: createGuid(),
          dev_type: control.dev_type ?? null,
          dev_addr: control.dev_addr ?? null,
          dev_loop: control.dev_loop ?? null,
          dev_data: control.dev_data ?? null,
          type_h: control.type_h ?? null,
          action_type: '设备回路状态',
          loop_name: getLoopNameWithFallback(deviceId, control.dev_type, control.dev_addr, control.dev_loop),
          details
        });
      });
    }
  }

  return rows;
};

export const processKafkaMessage = async ({ message }) => {
  let rows;
  try {
    const rawPayload = parseKafkaPayload(message.value);
    // Validate through Zod to get normalized fields (arrays defaulted, types coerced)
    const validatedPayload = kafkaPayloadSchema.parse(rawPayload);
    rows = buildRowsFromPayload(rawPayload);
    return { rows, payload: validatedPayload };
  } catch (error) {
    error.type = 'PARSE_ERROR';
    const rawValue = Buffer.isBuffer(message.value)
      ? message.value.toString('utf8')
      : String(message.value ?? '');
    error.rawPayload = rawValue.length > 1000 ? `${rawValue.slice(0, 1000)}...` : rawValue;
    if (error?.issues) {
      error.validationIssues = error.issues;
    }
    throw error;
  }
};
