import {
  Body,
  Controller,
  Headers,
  HttpCode,
  Post,
  Req,
} from '@nestjs/common';
import type { Request } from 'express';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { Public } from '../../../common/decorators/public.decorator';
import { HmacVerifier } from './hmac-verifier.service';
import { PushReceiverService } from './push-receiver.service';
import {
  PushEventsRequestSchema,
  type PushEventsRequest,
  PushRowsRequestSchema,
  type PushRowsRequest,
} from './push.schema';

/**
 * PushController — 宿主主动推送事件入口(webhook)。
 *
 * 端点:POST /pac/v1/push/events
 *
 * Headers(必填):
 *   X-PAC-Host-Id: <uuid>
 *   X-PAC-Timestamp: <unix-seconds>
 *   X-PAC-Signature: <hex hmac-sha256(timestamp + "." + body, secret)>
 *
 * Body: { events: [{ subjectType, subjectRef, patientRef?, clinicId, action, sourceEventId, occurredAt, payload }] }
 *
 * 鉴权走 @Public + HMAC,不走 JWT(宿主推送不是用户登录)。
 *
 * **挂上 rawBody parser**:NestJS 默认 Express body-parser 会 JSON.parse 掉 body,
 * 但 HMAC 必须用原始 raw bytes 验签。需要在 main.ts 配 express.raw 或 verify 钩子保留 raw body。
 * 当前实现:从 req.rawBody 取(若 main.ts 已配);未配则 fallback 到 JSON.stringify(body) — 字段顺序不一致会 break。
 */
@ApiTags('push')
@Controller('push')
export class PushController {
  constructor(
    private readonly verifier: HmacVerifier,
    private readonly receiver: PushReceiverService,
  ) {}

  @Post('events')
  @Public()
  @HttpCode(200)
  @ApiOperation({
    summary: '宿主推送事件 webhook(HMAC 验签)',
    description: 'Headers 必填 X-PAC-Host-Id / X-PAC-Timestamp / X-PAC-Signature。',
  })
  async receive(
    @Req() req: Request & { rawBody?: Buffer },
    @Headers('x-pac-host-id') hostIdHeader: string | undefined,
    @Headers('x-pac-timestamp') timestampHeader: string | undefined,
    @Headers('x-pac-signature') signatureHeader: string | undefined,
    @Body() body: PushEventsRequest,
  ) {
    // raw body 优先(main.ts 应配 rawBody preserve);否则用 JSON.stringify(已 parsed)兜底
    const rawBody = req.rawBody ? req.rawBody.toString('utf8') : JSON.stringify(body);

    const { hostId, hostName } = await this.verifier.verify({
      hostIdHeader,
      timestampHeader,
      signatureHeader,
      rawBody,
    });

    // body 单独走 zod 校验(Nest 全局 ZodValidationPipe 已挂)
    // 集团 tenantId 由事件携带(receiver 内解析校验),不再由 host 名兜底。
    const parsed = PushEventsRequestSchema.parse(body);

    return this.receiver.receive({
      hostId,
      hostName,
      events: parsed.events,
    });
  }

  /**
   * 形态 A:推原生表行(推荐路径)。
   *
   * Body: { tenantId?, source, rows: [{原生字段照抄}, ...] }
   * 复用 pull/cold-import 同一条摄入管线(transforms + assembler + 版本流),
   * 落库后对 touched 患者入队画像重算(plan 由定时任务兜底)。
   */
  @Post('rows')
  @Public()
  @HttpCode(200)
  @ApiOperation({
    summary: '宿主推送原生表行 webhook(形态 A,HMAC 验签)',
    description: 'Headers 必填 X-PAC-Host-Id / X-PAC-Timestamp / X-PAC-Signature。Body: { tenantId?, source, rows[] }。',
  })
  async receiveRows(
    @Req() req: Request & { rawBody?: Buffer },
    @Headers('x-pac-host-id') hostIdHeader: string | undefined,
    @Headers('x-pac-timestamp') timestampHeader: string | undefined,
    @Headers('x-pac-signature') signatureHeader: string | undefined,
    @Body() body: PushRowsRequest,
  ) {
    const rawBody = req.rawBody ? req.rawBody.toString('utf8') : JSON.stringify(body);

    const { hostId, hostName } = await this.verifier.verify({
      hostIdHeader,
      timestampHeader,
      signatureHeader,
      rawBody,
    });

    const parsed = PushRowsRequestSchema.parse(body);

    return this.receiver.receiveRows({
      hostId,
      hostName,
      source: parsed.source,
      rows: parsed.rows,
    });
  }
}
