import { Injectable, NotFoundException } from '@nestjs/common';
import type { PatientTimelineResponse } from '@pac/types';
import { FactStatus } from '@pac/types';
import { PrismaService } from '../../prisma/prisma.service';
import type { TenantScopeContext } from '../../common/decorators/tenant-scope.decorator';

/**
 * PatientService — 患者主档 + timeline
 *
 * timeline 读 patient_facts 的 active 版本(superseded / cancelled 不返回),
 * 按"时间锚点"排序:actual 用 occurredAt;planned 用 plannedFor;两者并集 desc。
 *
 * 数据来源:cold-import / pull / push 三入口 → TransactionSynthesizer 落 transactions
 *          → ParserPipeline 衍生 patient_facts(版本流)。
 */
/** find_patient 返回的极简候选卡片(消歧用;手机号掩码,真号走 revealPhone)。 */
export interface PatientSearchItem {
  id: string;
  externalId: string;
  name: string | null;
  phoneMasked: string | null;
  gender: string | null;
  birthDate: string | null;
  status: 'active' | 'archived';
}

/** 手机号掩码:13812343405 → 138****3405。 */
function maskPhone(phone: string | null): string | null {
  if (!phone) return null;
  const d = phone.replace(/\D/g, '');
  if (d.length < 7) return '***';
  return `${d.slice(0, 3)}****${d.slice(-4)}`;
}

@Injectable()
export class PatientService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * 患者检索(find_patient 背后)— 按 姓名/手机/externalId 模糊匹配,强制 tenant scope。
   * 返回极简候选卡片供 agent 消歧;**手机号掩码**(真号需另调 revealPhone,受审计)。
   */
  async search(
    scope: TenantScopeContext,
    query: string,
    limit = 10,
  ): Promise<PatientSearchItem[]> {
    const q = query.trim();
    if (!q) return [];
    const take = Math.min(Math.max(limit, 1), 50);
    const rows = await this.prisma.patient.findMany({
      where: {
        hostId: scope.hostId,
        tenantId: scope.tenantId,
        // 集团模型:数据范围在患者粒度按品牌圈(sourceUnits 空 = 不限,集团级角色)
        ...(scope.sourceUnits.length ? { sourceUnit: { in: scope.sourceUnits } } : {}),
        OR: [
          { name: { contains: q, mode: 'insensitive' } },
          { phone: { contains: q } },
          { externalId: { contains: q } },
        ],
      },
      select: {
        id: true,
        externalId: true,
        name: true,
        phone: true,
        gender: true,
        birthDate: true,
        active: true,
      },
      orderBy: { updatedAt: 'desc' },
      take,
    });
    return rows.map((p) => ({
      id: p.id,
      externalId: p.externalId,
      name: p.name,
      phoneMasked: maskPhone(p.phone),
      gender: p.gender,
      birthDate: p.birthDate ? p.birthDate.toISOString().slice(0, 10) : null,
      status: p.active ? 'active' : 'archived',
    }));
  }

  /**
   * Reveal — 拉患者真实手机号(掩码 138****3405 → 13812343405)。
   *
   * 仅 PATIENT_VIEW 权限的用户可调,且强制 tenant scope 校验。
   *
   * TODO(W5+ 生产前):落 audit log(patient_pii_reveal_logs 表)
   *   记录 { userId, patientId, field='phone', revealedAt, scope:plan/timeline/manual };
   *   合规审计能反查"谁何时看了哪位患者的电话"。POC 阶段先口头记录。
   */
  async revealPhone(
    scope: TenantScopeContext,
    patientId: string,
  ): Promise<{ phone: string | null }> {
    const patient = await this.prisma.patient.findFirst({
      where: {
        id: patientId,
        hostId: scope.hostId,
        tenantId: scope.tenantId,
        ...(scope.sourceUnits.length ? { sourceUnit: { in: scope.sourceUnits } } : {}),
      },
      select: { phone: true },
    });
    if (!patient) {
      throw new NotFoundException(`patient ${patientId} not found in scope`);
    }
    return { phone: patient.phone };
  }

  async getTimeline(
    scope: TenantScopeContext,
    patientId: string,
    limit: number,
  ): Promise<PatientTimelineResponse> {
    const patient = await this.prisma.patient.findFirst({
      where: {
        id: patientId,
        hostId: scope.hostId,
        tenantId: scope.tenantId,
        ...(scope.sourceUnits.length ? { sourceUnit: { in: scope.sourceUnits } } : {}),
      },
    });
    if (!patient) {
      throw new NotFoundException(`patient ${patientId} not found in scope`);
    }

    // active 版本(每 subject_id 唯一);
    // 按时间锚点 desc 排序:actual 用 occurredAt,planned 用 plannedFor。
    const facts = await this.prisma.patientFact.findMany({
      where: {
        hostId: scope.hostId,
        tenantId: scope.tenantId,
        patientId: patient.id,
        status: FactStatus.ACTIVE,
      },
      orderBy: [
        // actual 用 occurredAt;planned 该字段空 → nulls:last 让 actual 落前
        { occurredAt: { sort: 'desc', nulls: 'last' } },
        // planned 用 plannedFor(actual 该字段空,nulls 不影响)
        { plannedFor: { sort: 'desc', nulls: 'last' } },
        { createdAt: 'desc' },
      ],
      take: limit,
    });

    return {
      patient: {
        id: patient.id,
        externalId: patient.externalId,
        name: patient.name,
        phone: patient.phone,
        gender: patient.gender,
        birthDate: patient.birthDate ? patient.birthDate.toISOString() : null,
        status: patient.active ? 'active' : 'archived',
      },
      facts: facts.map((f) => ({
        id: f.id,
        subjectId: f.subjectId,
        kind: f.kind as PatientTimelineResponse['facts'][number]['kind'],
        type: f.type,
        status: f.status as PatientTimelineResponse['facts'][number]['status'],
        version: f.version,
        clinicId: f.clinicId,
        occurredAt: f.occurredAt ? f.occurredAt.toISOString() : null,
        plannedFor: f.plannedFor ? f.plannedFor.toISOString() : null,
        validFrom: f.validFrom ? f.validFrom.toISOString() : null,
        validUntil: f.validUntil ? f.validUntil.toISOString() : null,
        title: f.title,
        summary: f.summary,
        content: f.content as Record<string, unknown>,
        transactionIds: f.transactionIds,
        createdAt: f.createdAt.toISOString(),
        updatedAt: f.updatedAt.toISOString(),
        supersededAt: f.supersededAt ? f.supersededAt.toISOString() : null,
      })),
    };
  }
}
