import { Injectable, Logger } from '@nestjs/common';
import type { Action } from '@pac/types';
import { ParserRegistry } from './parsers/parser.registry';
import { FactWriter, FactWriteResult } from './fact-writer.service';

/**
 * ParserPipeline — transaction → fact 衍生编排器
 *
 * 输入:已写库的 transaction(含 id)+ 对应 canonical row
 * 流程:
 *   1. 按 transaction.action 查 ParserRegistry
 *   2. parser.parse(ctx) → FactDraft[]
 *   3. 逐个调用 FactWriter.writeDraft
 *   4. 汇总 metrics(created / superseded / unchanged / evidence_appended)
 *
 * 设计:
 *   - 无 parser 的 action 静默跳过(返回 zero metrics)— transaction 已落账,稍后补 parser 重放即可
 *   - 单条 transaction 的多 fact 衍生**不开外层事务** — 每 draft 一次 $transaction(FactWriter 内部)
 *     原因:外层事务跨多 subject 会放大锁范围;每 fact 独立写更适配未来 BullMQ jobName=subject_id 串行化
 *   - parser 抛错时单条 transaction 整体计 failed,不影响后续 transaction(由上层捕获)
 */
@Injectable()
export class ParserPipeline {
  private readonly logger = new Logger(ParserPipeline.name);

  constructor(
    private readonly registry: ParserRegistry,
    private readonly writer: FactWriter,
  ) {}

  /**
   * @returns PipelineRunMetrics 本次 transaction 衍生统计(单条 transaction)
   */
  async runForTransaction(input: {
    transaction: {
      id: string;
      hostId: string;
      tenantId: string;
      patientId: string | null;
      action: Action;
      subjectType: string;
      subjectId: string;
      occurredAt: Date;
      clinicId: string;
    };
    canonicalRow: Record<string, unknown>;
  }): Promise<PipelineRunMetrics> {
    const { transaction, canonicalRow } = input;
    const parser = this.registry.get(transaction.action);
    const metrics: PipelineRunMetrics = {
      action: transaction.action,
      parserMatched: !!parser,
      factsCreated: 0,
      factsSuperseded: 0,
      factsUnchanged: 0,
      factsEvidenceAppended: 0,
      factsFailed: 0,
      writes: [],
    };

    if (!parser) {
      this.logger.debug(
        `no parser for action=${transaction.action};skip(transaction=${transaction.id})`,
      );
      return metrics;
    }

    if (!transaction.patientId) {
      this.logger.warn(
        `parser pipeline: transaction ${transaction.id} has no patientId;skip fact derivation`,
      );
      return metrics;
    }

    const drafts = parser.parse({ transaction, canonicalRow });

    for (const draft of drafts) {
      try {
        const result = await this.writer.writeDraft({
          draft,
          hostId: transaction.hostId,
          tenantId: transaction.tenantId,
          patientId: transaction.patientId,
          transactionId: transaction.id,
        });
        metrics.writes.push(result);
        switch (result.action) {
          case 'created':
            metrics.factsCreated++;
            break;
          case 'superseded':
            metrics.factsSuperseded++;
            break;
          case 'unchanged':
            metrics.factsUnchanged++;
            break;
          case 'evidence_appended':
            metrics.factsEvidenceAppended++;
            break;
        }
      } catch (err) {
        metrics.factsFailed++;
        this.logger.error(
          `fact write failed: subjectId=${draft.subjectId} type=${draft.type} err=${
            err instanceof Error ? err.message : String(err)
          }`,
        );
      }
    }

    return metrics;
  }
}

export interface PipelineRunMetrics {
  action: string;
  parserMatched: boolean;
  factsCreated: number;
  factsSuperseded: number;
  factsUnchanged: number;
  factsEvidenceAppended: number;
  factsFailed: number;
  writes: FactWriteResult[];
}
