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

/**
 * M-AI-04-04: Active Recall Result Projector
 *
 * 将验证后的 ActiveRecall AI 输出原子投影到现有业务模型。
 *
 * 在 Prisma Transaction 内与 AiJobArtifact + markSucceeded 共享事务：
 *   1. AiAnalysisResult — 分析结果（upsert by deterministic ID）
 *   2. FocusItem — 每个薄弱项创建一条（事务内 findFirst + create）
 *   3. ReviewCard — 从 reviewSuggestion 创建（不需要额外 AI 调用）
 *   4. AiJobArtifact — 上述每个实体一条引用
 *
 * 幂等策略：
 *   - 入口幂等：项目启动时检查已有 Artifact → 直接返回
 *   - AiAnalysisResult：deterministic ID（ar_<jobId>）+ upsert
 *   - FocusItem：事务内 findFirst + create（Job 锁保证无并发写入同一 jobId）
 *   - ReviewCard：事务内 findFirst + create（每 job 最多 1 张）
 *
 * 与旧链路的差异：
 *   - 旧链路 ReviewCard 通过 EventBus 异步 + 二次 AI 调用生成
 *   - 本 Projector 直接从 validatedOutput.reviewSuggestion 创建 ReviewCard
 *     → 无二次 AI 调用，更可靠且原子
 */

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

  async project(
    tx: Prisma.TransactionClient,
    context: ProjectionContext,
  ): Promise<ArtifactReference[]> {
    const { job, validatedOutput } = 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(
        `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. AiAnalysisResult（分析结果）
    // 使用 deterministic ID 实现 upsert（无 Migration 下的幂等方案）
    // ═════════════════════════════════════════════════════════

    const resultId = deterministicResultId(job.id);
    await tx.aiAnalysisResult.upsert({
      where: { id: resultId },
      create: {
        id: resultId,
        userId: job.userId,
        jobId: job.id,
        answerId: job.targetId ?? null,
        summary: validatedOutput.summary ?? '',
        masteryScore: validatedOutput.score ?? null,
        strengths: (validatedOutput.strengths ?? []) as any,
        weaknesses: (validatedOutput.weaknesses ?? []) as any,
        suggestions: (validatedOutput.focusItems ?? []) as any,
        nextActions: (validatedOutput.reviewSuggestion ?? null) as any,
        rawResult: validatedOutput as any,
      },
      update: {
        // 已存在则更新（幂等重放时覆盖旧值）
        summary: validatedOutput.summary ?? '',
        masteryScore: validatedOutput.score ?? null,
        strengths: (validatedOutput.strengths ?? []) as any,
        weaknesses: (validatedOutput.weaknesses ?? []) as any,
        suggestions: (validatedOutput.focusItems ?? []) as any,
        nextActions: (validatedOutput.reviewSuggestion ?? null) as any,
        rawResult: validatedOutput as any,
      },
    });

    await tx.aiJobArtifact.create({
      data: {
        jobId: job.id,
        artifactType: 'AiAnalysisResult',
        artifactId: resultId,
        ordinal: ordinal++,
        metadata: { score: validatedOutput.score } as any,
      },
    });
    artifacts.push({ artifactType: 'AiAnalysisResult', artifactId: resultId, ordinal: ordinal - 1 });

    this.logger.log(`Projector: AiAnalysisResult ${resultId} written for job=${job.id}`);

    // ═════════════════════════════════════════════════════════
    // 2. FocusItem（从 AI 输出的 focusItems 结构化数据创建）
    //    优于旧链路用 weaknesses 字符串创建 — 可写入 reason/suggestion/priority
    // ═════════════════════════════════════════════════════════

    const focusItems: any[] = validatedOutput.focusItems ?? [];
    for (const fi of focusItems) {
      const title = fi?.title;
      if (!title || typeof title !== 'string' || title.trim().length === 0) continue;

      // 幂等：同一 userId + title + source 不重复创建
      const existingFi = await tx.focusItem.findFirst({
        where: { userId: job.userId, title, source: 'ai-analysis' },
      });
      if (existingFi) {
        await upsertArtifact(tx, job.id, 'FocusItem', existingFi.id, ordinal);
        artifacts.push({ artifactType: 'FocusItem', artifactId: existingFi.id, ordinal: ordinal++ });
        continue;
      }

      const record = await tx.focusItem.create({
        data: {
          userId: job.userId,
          title,
          reason: fi.reason || '',
          suggestion: fi.suggestion || '',
          priority: fi.priority || 'normal',
          status: 'open',
          source: 'ai-analysis',
        },
      });

      await upsertArtifact(tx, job.id, 'FocusItem', record.id, ordinal);
      artifacts.push({ artifactType: 'FocusItem', artifactId: record.id, ordinal: ordinal++ });
    }

    if (focusItems.length > 0) {
      this.logger.log(`Projector: ${focusItems.length} FocusItem(s) written for job=${job.id}`);
    }

    // ═════════════════════════════════════════════════════════
    // 3. ReviewCard（从 reviewSuggestion 直接创建，无二次 AI 调用）
    // ═════════════════════════════════════════════════════════

    const reviewSuggestion = validatedOutput.reviewSuggestion as any;
    if (reviewSuggestion?.shouldReview) {
      // 幂等：每 job 最多 1 张 ReviewCard
      const existingCard = await tx.reviewCard.findFirst({
        where: { userId: job.userId },
        orderBy: { createdAt: 'desc' },
      });

      // 如果最近的一张卡片内容相同则跳过（简单幂等检查）
      const cardFront = reviewSuggestion.cardFront || '请回顾薄弱知识点';
      const cardBack = reviewSuggestion.cardBack || '';
      const intervalDays = reviewSuggestion.intervalDays || 1;

      const skip =
        existingCard &&
        existingCard.frontText === cardFront &&
        existingCard.backText === cardBack;

      if (!skip) {
        const card = await tx.reviewCard.create({
          data: {
            userId: job.userId,
            frontText: cardFront,
            backText: cardBack,
            difficulty: 'normal',
            status: 'active',
            intervalDays: Math.min(365, Math.max(1, intervalDays)),
            easeFactor: 2.5,
            repetitionCount: 0,
            lapseCount: 0,
            scheduleState: 'new',
            nextReviewAt: new Date(Date.now() + intervalDays * 86400000),
          },
        });

        await upsertArtifact(tx, job.id, 'ReviewCard', card.id, ordinal);
        artifacts.push({ artifactType: 'ReviewCard', artifactId: card.id, ordinal: ordinal++ });

        this.logger.log(`Projector: ReviewCard ${card.id} written for job=${job.id}`);
      }
    }

    // ═════════════════════════════════════════════════════════
    // 完成
    // ═════════════════════════════════════════════════════════

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

// ── Helpers ──

/** 从 jobId 派生确定性 AiAnalysisResult ID */
function deterministicResultId(jobId: string): string {
  // jobId 格式: cuid (25 chars)，取前 24 字符 + "ar_" 前缀
  return `ar_${jobId.substring(0, 24)}`;
}

/** 幂等写入 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;
  }
}
