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

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

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

    // Entry idempotency
    const existing = await tx.aiJobArtifact.findMany({ where: { jobId: job.id }, orderBy: { ordinal: 'asc' } });
    if (existing.length > 0) {
      this.logger.log(`LearningAnalysis Projector: returning ${existing.length} existing artifact(s) for job=${job.id}`);
      return existing.map(a => ({ artifactType: a.artifactType, artifactId: a.artifactId, ordinal: a.ordinal }));
    }

    const output = validatedOutput as any;
    const snap = snapshot?.snapshot || {};

    // ── 1. AiLearningAnalysis ──
    const analysis = await tx.aiLearningAnalysis.create({
      data: {
        userId: job.userId,
        jobId: job.id,
        snapshotId: job.snapshotId || null,
        targetType: job.targetType || 'knowledge_base',
        targetId: job.targetId || 'unknown',
        learningState: output.insufficientData ? 'insufficient' : 'analyzed',
        summary: output.summary || null,
        riskLevel: this.computeRiskLevel(output.risks || []),
        confidence: output.confidence ?? null,
        evidence: (output.strengths || []).concat(output.weaknesses || []) as any,
        nextActionIds: (output.recommendations || []).map((r: any) => r.actionType) as any,
        promptVersion: job.promptVersion || null,
        schemaVersion: job.outputSchemaVersion || null,
      },
    });

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

    this.logger.log(`LearningAnalysis Projector: AiLearningAnalysis ${analysis.id} written for job=${job.id}`);

    // ── 2. WeakPointCandidate x N ──
    for (const w of output.weaknesses || []) {
      const candidate = await tx.weakPointCandidate.create({
        data: {
          userId: job.userId,
          jobId: job.id,
          snapshotId: job.snapshotId || null,
          targetType: job.targetType || 'knowledge_base',
          targetId: job.targetId || 'unknown',
          knowledgePointId: w.knowledgePointId || null,
          title: w.title,
          reason: w.description || null,
          confidence: output.confidence ?? null,
          evidence: w.evidenceRefs || null,
          status: 'active',
        },
      });

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

    if ((output.weaknesses || []).length > 0) {
      this.logger.log(`LearningAnalysis Projector: ${output.weaknesses.length} WeakPointCandidate(s) written for job=${job.id}`);
    }

    // ── 3. NextActionRecommendation x N ──
    for (const r of output.recommendations || []) {
      const rec = await tx.nextActionRecommendation.create({
        data: {
          userId: job.userId,
          jobId: job.id,
          snapshotId: job.snapshotId || null,
          actionType: r.actionType || 'general',
          targetType: r.targetType || null,
          targetId: r.targetId || null,
          title: r.title,
          reason: r.reason || null,
          priority: r.priority ?? 0,
          estimatedMinutes: r.estimatedMinutes ?? null,
          status: 'active',
        },
      });

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

    if ((output.recommendations || []).length > 0) {
      this.logger.log(`LearningAnalysis Projector: ${output.recommendations.length} Recommendation(s) written for job=${job.id}`);
    }

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

  private computeRiskLevel(risks: any[]): string | null {
    if (!risks || risks.length === 0) return null;
    if (risks.some((r: any) => r.severity === 'high')) return 'high';
    if (risks.some((r: any) => r.severity === 'medium')) return 'medium';
    return 'low';
  }
}

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) { if (err?.code === 'P2002') return; throw err; }
}
