import { Inject, Post, Get, Body, Headers, Fields, Files } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { PushOpenService } from '../../service/open';
import { WeightUploadDto } from '../../dto/weight.dto';
import * as crypto from 'crypto';
import { BaseSysParamService } from '../../../base/service/sys/param';
import { Context } from '@midwayjs/koa'; //

/**
 * 开放无需验证的上报数据接口
 */
@CoolController({
  prefix: '/open/push',
})
export class AppPushOpenController extends BaseController {
  @Inject()
  ctx: Context;

  @Inject()
  pushOpenService: PushOpenService;

  @Inject()
  baseSysParamService: BaseSysParamService;


  @Post('/weight', { summary: '原始称重上报接口' })
  async uploadWeight(
    @Headers() headers: any,
    @Body() data: WeightUploadDto,
  ) {

    const CLIENT_ID = String(await this.baseSysParamService.dataByKey('yaohua.weight.clientid')); //'10001';
    const SECRET = String(await this.baseSysParamService.dataByKey('yaohua.weight.secret')); //'9001ac9676b8';


    const clientId = headers['clientid'];
    const sign = headers['sign'];

    // 1. clientId 校验
    if (clientId !== CLIENT_ID) {
      await this.pushOpenService.writePushAuditLog("weight", { "deviceNo": clientId, "requestContent": data, "errorMessage": "非法 clientId" });
      return {
        code: 1001,
        msg: '非法 clientId',
        data: null,
      };
    }

    // 2. 签名校验
    const signStr =
      [
        clientId,
        data.deviceNo,
        data.uploadTime,
        data.garbageType,
        data.garbageWeight,
        SECRET,
      ].join(',');

    const realSign = crypto
      .createHash('sha256')
      .update(signStr)
      .digest('hex')
      .toLowerCase();

    if (realSign !== sign?.toLowerCase()) {
      await this.pushOpenService.writePushAuditLog("weight", { "deviceNo": data.deviceNo, "requestContent": data, "errorMessage": "签名验证失败" });

      return {
        code: 1002,
        msg: '签名验证失败',
        data: null,
      };
    }
    try {
      await this.pushOpenService.reportWeight(data);
      return {
        code: 0,
        msg: '',
        data: 'success',
      };
    } catch (err) {
      await this.pushOpenService.writePushAuditLog("weight", { "deviceNo": data.deviceNo, "requestContent": data, "errorMessage": "系统异常,写入失败." });

      return {
        code: 500,
        msg: '系统异常',
        data: null,
      };
    }
  }

  @Post('/yingshiyun', { summary: '萤石云Webhook上报接口' })
  async saveYingshiyunMessage() {
    const req = this.ctx.req;
    const rawBody = await new Promise<string>((resolve, reject) => {
      let data = '';
      const timer = setTimeout(() => reject(new Error('读取流超时')), 5000);
      req.on('data', chunk => data += chunk);
      req.on('end', () => {
        clearTimeout(timer);
        resolve(data);
      });
      req.on('error', err => {
        clearTimeout(timer);
        reject(err);
      });
    }).catch(err => {
      console.error('读取stream异常：', err);
      this.ctx.status = 200;
      this.ctx.body = { messageId: '' };
      return '';
    });
    if (!rawBody) {
      this.ctx.status = 200;
      this.ctx.body = { messageId: '' };
      return;
    }
    console.log('【萤石云原始报文】:', rawBody.slice(0, 500));
    let msg: any;
    try {
      msg = JSON.parse(rawBody);
    } catch (e) {
      console.error('JSON解析失败:', e);
      this.ctx.status = 200;
      this.ctx.body = { messageId: '' };
      return;
    }
    const messageId = msg?.header?.messageId;
    const msgType = msg?.header?.type; // "ys.onoffline" / "ys.alarm" / ...
    console.log('消息类型:', msgType, '| messageId:', messageId);
    if (msgType === 'ys.alarm') {
      console.log('告警类型:', msg.body?.alarmType, '| 描述:', msg.body?.describe);
    } else if (msgType === 'ys.onoffline') {
      console.log('设备上下线:', msg.body?.msgType, '| 设备:', msg.body?.deviceName);
    }
    try {
      await this.pushOpenService.saveYingshiyunMessage(msg);
    } catch (e) {
      console.error('数据处理失败:', e);
      // 即使存库失败，也要回 200 + messageId，避免萤石重试
    }
    this.ctx.status = 200;
    this.ctx.set('Content-Type', 'application/json');
    this.ctx.body = { messageId: messageId || '' };
  }


  /**
   * 摄像机主动推送告警事件（移动侦测VMD、人体侦测，XML multipart格式）
   * 地址：POST /open/push/cameraEvent
   */
  @Post('/cameraEvent', { summary: '摄像头主动上报告警事件' })
  async receiveCameraEvent() {
    const req = this.ctx.req;
    let rawBody = '';

    try {
      rawBody = await new Promise<string>((resolve, reject) => {
        let data = '';
        const timer = setTimeout(() => reject(new Error('读取流超时')), 5000);
        req.on('data', (chunk) => (data += chunk));
        req.on('end', () => {
          clearTimeout(timer);
          resolve(data);
        });
        req.on('error', (err) => {
          clearTimeout(timer);
          reject(err);
        });
      });
    } catch (err) {
      console.error('【摄像头事件】读取stream异常：', err);
      this.ctx.status = 200;
      this.ctx.body = { code: -1, msg: 'read error' };
      return;
    }
    if (!rawBody) {
      this.ctx.status = 200;
      this.ctx.body = { code: -1, msg: 'empty body' };
      return;
    }

    // 截取完整XML文本
    const xmlReg = /<\?xml[\s\S]*?<\/EventNotificationAlert>/;
    const xmlMatch = rawBody.match(xmlReg);
    if (!xmlMatch) {
      console.warn('【摄像头事件】未匹配到告警XML内容');
      this.ctx.status = 200;
      this.ctx.body = { code: -1 };
      return;
    }
    const xmlStr = xmlMatch[0];

    // 通用XML节点提取（支持标签前后换行空格）
    const getVal = (xml: string, tag: string) => {
      const reg = new RegExp(`<${tag}>\\s*([\\s\\S]*?)\\s*<\\/${tag}>`);
      const res = xml.match(reg);
      return res ? res[1].trim() : '';
    };

    // 组装告警实体
    const cameraEvent = {
      ipAddress: getVal(xmlStr, 'ipAddress'),
      macAddress: getVal(xmlStr, 'macAddress'),
      portNo: getVal(xmlStr, 'portNo'),
      channelID: getVal(xmlStr, 'channelID'),
      channelName: getVal(xmlStr, 'channelName'),
      dateTime: getVal(xmlStr, 'dateTime'),
      eventType: getVal(xmlStr, 'eventType'), // VMD=移动侦测
      eventState: getVal(xmlStr, 'eventState'),
      eventDescription: getVal(xmlStr, 'eventDescription'),
      targetType: getVal(xmlStr, 'targetType'), // human 人体
      targetID: getVal(xmlStr, 'targetID'),
      // 目标坐标框
      rectX: getVal(xmlStr, 'X'),
      rectY: getVal(xmlStr, 'Y'),
      rectWidth: getVal(xmlStr, 'width'),
      rectHeight: getVal(xmlStr, 'height'),
    };

    try {
      // 调用service处理摄像头告警事件，建议新建service方法
      const result = await this.pushOpenService.saveCameraAlarmEvent(cameraEvent);

      this.ctx.status = 200;
      this.ctx.set('Content-Type', 'application/json');
      this.ctx.body = { success: true, messageId: result };
    } catch (serviceErr) {
      console.error('【摄像头事件】业务处理失败：', serviceErr);
      this.ctx.status = 200;
      this.ctx.body = { code: -1, msg: 'handle fail' };
    }
  }
}