import { Injectable, Logger, NotFoundException } from '@nestjs/common';
import { randomUUID } from 'node:crypto';
import { maskName } from '@pac/utils';
import { PERSONA_FEATURE_SPECS } from '@pac/types';
import { PrismaService } from '../../../prisma/prisma.service';
import { AiCallRunnerService } from '../ai-call-runner.service';
import { DraftPersonaSummaryCall } from '../calls/draft-persona-summary/call';
import type { DraftPersonaSummaryInput } from '../calls/draft-persona-summary/input.types';
import type { TenantScopeContext } from '../../../common/decorators/tenant-scope.decorator';

export interface PersonaSummaryResult {
  /** 一句话画像重点;status='empty' 时为 null */
  summary: string | null;
  status: 'ready' | 'empty';
  source?: 'agent' | 'template_fallback';
}

/**
 * PersonaSummaryOrchestrator — 画像标签「一句话重点」get-or-generate。
 *
 *   有 ready 的 plan_summaries[persona_tags] → 直接返回(秒回,不调 LLM)。
 *   没有 → 取该 patient 当前 persona 的 features → 跑 AiCall → upsert 存库 → 返回。
 *   无 persona / 无 feature → status='empty'(前端继续展示标签云,不显示摘要)。
 *
 * 与 RecallSummaryOrchestrator(历史联系一句话摘要)完全平行,只换数据源 + AiCall。
 */
@Injectable()
export class PersonaSummaryOrchestrator {
  private readonly logger = new Logger(PersonaSummaryOrchestrator.name);
  private static readonly TYPE = 'persona_tags' as const;

  constructor(
    private readonly prisma: PrismaService,
    private readonly runner: AiCallRunnerService,
    private readonly call: DraftPersonaSummaryCall,
  ) {}

  async getOrGenerate(scope: TenantScopeContext, planId: string): Promise<PersonaSummaryResult> {
    const plan = await this.prisma.followupPlan.findUnique({
      where: { id: planId },
      select: { id: true, hostId: true, tenantId: true, patientId: true },
    });
    if (!plan || plan.hostId !== scope.hostId || plan.tenantId !== scope.tenantId) {
      throw new NotFoundException(`Plan ${planId} not found`);
    }

    // 1) 已生成 → 直接返回
    const existing = await this.prisma.planSummary.findUnique({
      where: { planId_type: { planId: plan.id, type: PersonaSummaryOrchestrator.TYPE } },
      select: { content: true, status: true, source: true },
    });
    if (existing?.status === 'ready' && existing.content) {
      return {
        summary: existing.content,
        status: 'ready',
        source: (existing.source as 'agent' | 'template_fallback' | null) ?? undefined,
      };
    }

    // 2) 取该患者当前 persona 的画像标签(key + description 取值)
    const persona = await this.prisma.persona.findFirst({
      where: { patientId: plan.patientId, supersededAt: null },
      orderBy: { version: 'desc' },
      select: { features: { orderBy: { createdAt: 'asc' }, select: { key: true, description: true } } },
    });
    const features = persona?.features ?? [];
    if (features.length === 0) return { summary: null, status: 'empty' };

    const patient = await this.prisma.patient.findUnique({
      where: { id: plan.patientId },
      select: { name: true },
    });

    const input: DraftPersonaSummaryInput = {
      patientNameMasked: maskName(patient?.name ?? null) ?? '该患者',
      items: features.map((f) => ({
        label: PERSONA_FEATURE_SPECS[f.key]?.nameZh ?? f.key,
        value: f.description,
      })),
    };

    // 3) 跑 AiCall → upsert
    const result = await this.runner.run(this.call, input, {
      hostId: plan.hostId,
      tenantId: plan.tenantId,
      workflowRunId: randomUUID(),
      linkedPatientId: plan.patientId,
      linkedPlanId: plan.id,
      bustCache: false,
      evalMode: 'production',
    });

    await this.prisma.planSummary.upsert({
      where: { planId_type: { planId: plan.id, type: PersonaSummaryOrchestrator.TYPE } },
      create: {
        hostId: plan.hostId,
        tenantId: plan.tenantId,
        planId: plan.id,
        type: PersonaSummaryOrchestrator.TYPE,
        content: result.output.summary,
        status: 'ready',
        source: result.source,
        agentInvocationId: result.invocationId,
      },
      update: {
        content: result.output.summary,
        status: 'ready',
        source: result.source,
        agentInvocationId: result.invocationId,
      },
    });

    return { summary: result.output.summary, status: 'ready', source: result.source };
  }
}
