import type { PullConfig } from '@pac/types';
import type { EmitsConfig } from '../assembler/assembler.schema';

/**
 * PullStrategy — "如何从一个 host 获取 raw 数据" 的扩展点(W5+ 真接 host 时实装)。
 *
 * v2 重要变化:strategy 只返回 **raw tables**(host 原生表结构),不再做 canonical 转换。
 * Canonical 转换全部由 AssemblerEngine(yaml 驱动)处理,跟 cold-import / push 共用。
 *
 * 起步 strategy 候选(都归档着,等真接 host 时唤醒):
 *   - api-key-pull       Bearer token 鉴权
 *   - http-login-pull    Session cookie 鉴权
 *   - oauth2-pull        OAuth2 / OIDC(W6+)
 *
 * (cold-import 不走 strategy 直接读文件;push 也不走 strategy;但 raw → canonical 都过 AssemblerEngine)
 */

export interface SyncContext {
  hostId: string;
  tenantId: string;
  pullConfig: PullConfig | null;
  /** correlates log rows for one pull run */
  runId: string;
}

/**
 * 一页 raw 数据 — strategy 拉一批回来 = 多张表的 raw 行。
 * 表名跟 manifest.tables[*].table / assembler.primary.table 对齐,由 AssemblerEngine 索引。
 *
 * **emitsPerResource**(v2.2):mock / shortcut adapter 返回 canonical 形态直接 dispatch 时填,
 * key = canonical resource(patients/appointments/encounters/...),value = 该资源的 emits。
 * 真实 adapter 返回 host raw tables 时该字段为空,orchestrator 跑 AssemblerEngine 从 yaml 取 emits。
 */
export interface RawPullPage {
  /// `{ tb_patient: [row, ...], tb_encounter: [...], ... }`
  tables: Record<string, unknown[]>;
  /// 下一页游标;null = 没更多
  nextCursor: string | null;
  /// 可选:adapter 自带 emits 注册(适用于直接返回 canonical 形态的 mock / shortcut adapter)
  emitsPerResource?: Record<string, EmitsConfig>;
}

/**
 * 单患者即时 pull 结果(客服工作台"刷新"按钮)。
 * `hostNotFound=true` 表示宿主 404,应用层据此降级处理(标 archived 等)。
 */
export interface RawPullPatientResult {
  tables: Record<string, unknown[]>;
  hostNotFound?: boolean;
  emitsPerResource?: Record<string, EmitsConfig>;
}

export interface PullStrategy {
  /** stable name for logging / metrics */
  readonly key: string;

  /** does this strategy apply to the given context? first match wins */
  appliesTo(ctx: SyncContext): boolean;

  /** pull one page newer than `cursor`. idempotent on retries. */
  pullPage(ctx: SyncContext, cursor: string | null): Promise<RawPullPage>;

  /**
   * Optional: 拉取指定时间窗的全部数据(Reconcile 用,**不走 cursor**)。
   * Adapter 不重写时,orchestrator 退化到反复 pullPage 直到 nextCursor=null。
   * Adapter 能实现"按 occurred_at 时间窗拉"(host API 支持 ?from=&to=)就实现此方法,
   * reconcile 才能精准对账过去 N 天。
   */
  pullWindow?(
    ctx: SyncContext,
    startedAt: Date,
    endedAt: Date,
  ): AsyncIterable<RawPullPage>;

  /**
   * Optional: pull all current state for a single patient(即时刷新)。
   * Adapter 不支持 → undefined,SyncService 抛 SYNC_PATIENT_PULL_NOT_SUPPORTED。
   */
  pullPatient?(ctx: SyncContext, patientExternalId: string): Promise<RawPullPatientResult>;

  /** optional warm-up before the loop(login / pool setup) */
  prepare?(ctx: SyncContext): Promise<void>;
}
