import { z } from 'zod';

/**
 * Push 事件 body 契约 — 宿主主动推送给 PAC。
 *
 * 一次请求可携带多条事件(批量推),按 occurredAt 顺序处理。
 * 每条事件 = 一个 patient_transactions 行(经过 synthesizer)。
 *
 * 字段已是 PAC canonical 命名:
 *   - subjectType(patient / appointment / encounter ...) → patient_transactions.subject_type
 *   - subjectRef → patient_transactions.subject_id
 *   - patientRef → 反查 patient.id(若 subjectType 不是 patient 本身)
 *   - sourceEventId → 幂等键
 *   - occurredAt → 时间戳
 *   - payload → 业务字段(canonical 已映射,如 appointmentAt / status / doctorId 等)
 */
export const PushEventSchema = z.object({
  subjectType: z.string().describe('CanonicalSubjectType: patient/appointment/...'),
  subjectRef: z.string().min(1).describe('宿主侧本资源的 externalId'),
  patientRef: z.string().min(1).optional().describe('关联 patient externalId;subjectType=patient 时可省'),
  clinicId: z.string().min(1).describe('发生诊所(立柱必填)'),
  action: z.string().describe('canonical Action: created/updated/canceled/...'),
  sourceEventId: z.string().min(1).describe('宿主侧唯一事件 id(幂等键)'),
  occurredAt: z.string().datetime({ offset: true }).describe('ISO 8601 with timezone'),
  eventSeq: z.number().int().nonnegative().optional().describe('递增序号,可选,用于水位'),
  payload: z.record(z.string(), z.unknown()).describe('已 canonical 化的业务字段'),
});

export type PushEvent = z.infer<typeof PushEventSchema>;

export const PushEventsRequestSchema = z.object({
  events: z.array(PushEventSchema).min(1).max(500),
});

export type PushEventsRequest = z.infer<typeof PushEventsRequestSchema>;

export const PushEventsResponseSchema = z.object({
  syncLogId: z.string().uuid(),
  accepted: z.number().int().describe('收到的事件总数'),
  transactionsWritten: z.number().int(),
  duplicates: z.number().int(),
  failed: z.number().int(),
});

export type PushEventsResponse = z.infer<typeof PushEventsResponseSchema>;

/**
 * 形态 A:推【原生表行】契约 — 宿主把"导出给数仓的那种原生表行"照抄推来。
 *
 *   { tenantId?, source, rows: [ {原生字段照抄...}, ... ] }
 *
 * - `source`  = 原生源表名(= 宿主数据字典里的表名 / manifest tables[].table)
 * - `rows`    = 原生行,PAC 用该 host 的 yaml 跑 transforms + assembler 拆分/映射/落库
 * - `tenantId`= 选填;PAC 默认按 manifest 的 brand→tenant 逐行解析(同 pull/reparse)
 *
 * 跟 pull / cold-import / reparse 走同一条摄入管线,只是数据入口是 webhook。
 */
export const PushRowsRequestSchema = z.object({
  tenantId: z.string().min(1).optional(),
  source: z.string().min(1).describe('原生源表名(= manifest tables[].table)'),
  rows: z
    .array(z.record(z.string(), z.unknown()))
    .min(1)
    .max(2000)
    .describe('原生表行,字段照抄'),
});

export type PushRowsRequest = z.infer<typeof PushRowsRequestSchema>;

export const PushRowsResponseSchema = z.object({
  syncLogId: z.string().uuid(),
  source: z.string(),
  accepted: z.number().int().describe('收到的原生行数'),
  transactionsWritten: z.number().int(),
  duplicates: z.number().int(),
  failed: z.number().int(),
  personaEnqueued: z.number().int().describe('入队画像重算的患者数(plan 不在此触发,由定时任务保)'),
});

export type PushRowsResponse = z.infer<typeof PushRowsResponseSchema>;
