import { Injectable, Logger } from '@nestjs/common';
import type { Prisma } from '@prisma/client';
import {
  ResultProjector,
  ProjectionContext,
  ArtifactReference,
} from './result-projector.interface';

/**
 * M-AI-06-05: ReviewCard Generation Result Projector
 *
 * 将验证后的 ReviewCard AI 输出原子投影到现有业务模型。
 *
 * 契约依据：docs/architecture/m-ai-06-review-card-child-job-contract.md §3, §6
 *
 * 在 Prisma Transaction 内与 AiJobArtifact + markSucceeded 共享事务：
 *   1. ReviewCard × N — 每张卡片一条记录
 *   2. AiJobArtifact × N — 每张卡片一条引用
 *
 * 幂等策略：
 *   - 入口幂等：检查已有 Artifact → 直接返回（与 FeynmanProjector 一致）
 *   - Artifact：upsertArtifact + P2002 catch（jobId + artifactType + artifactId 唯一约束）
 *   - Engine 级 CAS 锁保证无并发写入同一 jobId
 *
 * ReviewCard 默认值（与 Legacy 一致，契约 §6.2）：
 *   - status: 'active'
 *   - intervalDays: 1
 *   - easeFactor: 2.5
 *   - repetitionCount: 0
 *   - lapseCount: 0
 *   - scheduleState: 'new'
 *   - nextReviewAt: new Date()
 *
 * Parent 隔离：
 *   - 不修改 Parent Job 的 lifecycleStatus / errorCode / finishedAt / validatedOutput
 */

@Injectable()
export class ReviewCardGenerationProjector implements ResultProjector {
  readonly key = 'review_card_generation_projector';
  private readonly logger = new Logger(ReviewCardGenerationProjector.name);

  async project(
    tx: Prisma.TransactionClient,
    context: ProjectionContext,
  ): Promise<ArtifactReference[]> {
    const { job, validatedOutput, snapshot } = context;
    let ordinal = 0;

    // ── 入口幂等：已有 Artifact → 直接返回 ──
    const existingArtifacts = await tx.aiJobArtifact.findMany({
      where: { jobId: job.id },
      orderBy: { ordinal: 'asc' },
    });
    if (existingArtifacts.length > 0) {
      this.logger.log(
        `ReviewCard Projector: returning ${existingArtifacts.length} existing artifact(s) for job=${job.id}`,
      );
      return existingArtifacts.map((a) => ({
        artifactType: a.artifactType,
        artifactId: a.artifactId,
        ordinal: a.ordinal,
      }));
    }

    const artifacts: ArtifactReference[] = [];

    // ═════════════════════════════════════════════════════════
    // 1. ReviewCard × N
    //    从 validatedOutput.cards 逐条创建
    //    读取 knowledgeItemId 自 Snapshot（契约 §4.1）
    // ═════════════════════════════════════════════════════════

    const cards: any[] = validatedOutput.cards ?? [];
    const knowledgeItemId = snapshot?.snapshot?.knowledgeItemId ?? null;

    for (const card of cards) {
      // 创建 ReviewCard（与 Legacy review.service.ts:82-94 字段一致）
      const record = await tx.reviewCard.create({
        data: {
          userId: job.userId,
          knowledgeItemId,
          frontText: card.frontText,
          backText: card.backText ?? '',
          difficulty: card.difficulty ?? 'medium',
          status: 'active',
          intervalDays: 1,
          easeFactor: 2.5,
          repetitionCount: 0,
          lapseCount: 0,
          scheduleState: 'new',
          nextReviewAt: new Date(),
        },
      });

      // 幂等写入 Artifact（jobId + artifactType + artifactId 唯一约束）
      await upsertArtifact(tx, job.id, 'ReviewCard', record.id, ordinal);
      artifacts.push({
        artifactType: 'ReviewCard',
        artifactId: record.id,
        ordinal: ordinal++,
      });
    }

    if (cards.length > 0) {
      this.logger.log(
        `ReviewCard Projector: ${cards.length} ReviewCard(s) written for job=${job.id}`,
      );
    } else {
      this.logger.warn(
        `ReviewCard Projector: validatedOutput.cards is empty for job=${job.id}`,
      );
    }

    // ═════════════════════════════════════════════════════════
    // 2. Parent Job 隔离
    //    ★ 不修改 Parent Job 的任何字段（契约 §3.4）
    //    ★ ProjectionExecutor 负责在此 Projector 返回后调用 markSucceeded
    // ═════════════════════════════════════════════════════════

    this.logger.log(
      `ReviewCard Projector: ${artifacts.length} artifact(s) total for job=${job.id}`,
    );
    return artifacts;
  }
}

// ── Helpers ──

/** 幂等写入 Artifact（jobId + artifactType + artifactId 唯一约束） */
async function upsertArtifact(
  tx: Prisma.TransactionClient,
  jobId: string,
  artifactType: string,
  artifactId: string,
  ordinal: number,
): Promise<void> {
  try {
    await tx.aiJobArtifact.create({
      data: { jobId, artifactType, artifactId, ordinal },
    });
  } catch (err: any) {
    // P2002: 唯一约束冲突 → 已存在，幂等跳过
    if (err?.code === 'P2002') {
      return;
    }
    throw err;
  }
}
