<template>
  <!--
    AI辅助出方：单根 u-popup（Vue2 组件不能双根）
    生成确认用抽屉内全屏覆盖层，避免第二个 popup / page-container 点了无反应
    模板禁止 ?? ?. ；:class 禁止方法调用
  -->
  <u-popup
    v-model="visible"
    mode="bottom"
    border-radius="24"
    :safe-area-inset-bottom="true"
    :mask-close-able="true"
    height="82%"
    z-index="10090"
    @close="onClose"
  >
    <view class="ai-rx-drawer">
      <view class="ai-rx-title-row">
        <text class="ai-rx-title">AI辅助出方</text>
        <text class="ai-rx-sub">{{ patientName || '—' }} · {{ sexLabel }} · {{ patientAge || 0 }}岁</text>
      </view>
      <view v-if="disclaimerText && !consentVisible" class="ai-disclaimer">
        <text class="ai-disclaimer-title">重要提示</text>
        <text class="ai-disclaimer-body">{{ disclaimerText }}</text>
      </view>
      <view v-if="consentVisible" class="ai-consent-panel">
        <text class="fs-30 font-bold">AI 使用知情同意</text>
        <scroll-view scroll-y class="ai-consent-scroll">
          <text class="ai-consent-body">{{ consentText }}</text>
        </scroll-view>
      </view>
      <view v-else-if="conflictView.length && !genModalVisible" class="ai-conflict">
        <text class="ai-conflict-title">配伍禁忌提示</text>
        <view v-for="(line, ci) in conflictView" :key="ci" class="ai-conflict-line">· {{ line }}</view>
      </view>
      <!-- 主内容：历史 + 对照 -->
      <view v-if="!consentVisible && !genModalVisible" class="ai-rx-body flex-row">
        <scroll-view scroll-y class="ai-rx-hist">
          <view class="ai-hist-hd">
            <text class="fs-24 font-bold">历史</text>
          </view>
          <view v-if="histLoading" class="p-24 color-sub fs-24">加载中…</view>
          <view v-else-if="historyEmpty" class="p-24 color-sub fs-22">暂无记录，可生成处方</view>
          <view
            v-for="row in historyView"
            :key="row.id"
            class="ai-hist-item"
            :class="{ 'ai-hist-item--active': activeId == row.id }"
            :data-id="row.id"
            @click="onSelectHistoryTap"
          >
            <text class="fs-26 font-bold ai-hist-name">{{ row.nameText }}</text>
            <text class="fs-22 color-sub">{{ row.typeText }}</text>
          </view>
        </scroll-view>
        <scroll-view scroll-y class="ai-rx-match">
          <!-- AI 专属 Loading：仅生成中；历史对照用普通文案 -->
          <ai-generation-loading
            v-if="generating"
            type="prescription"
            text="AI正在生成处方"
          />
          <view v-else-if="matchLoading" class="p-32 color-sub fs-24">加载历史处方…</view>
          <block v-else>
            <view v-if="!matchResult && !lastDurationMs" class="p-32 color-sub fs-24">请选择历史或生成处方</view>
            <view v-else-if="!matchResult && lastDurationMs" class="p-32 color-sub fs-24">
              本次生成未产出对照结果（耗时 {{ durationText }}）
            </view>
            <block v-else>
              <view class="ai-sec-title">已对照（{{ matchedCount }}）</view>
              <view v-if="isTcmRx" class="ai-tcm-grid">
                <view v-for="m in matchedView" :key="m.mi" class="ai-tcm-cell">
                  <view class="flex-row flex-ali-center flex-jus-sp">
                    <text class="fs-24 font-bold ai-match-name">{{ m.ai_name }}</text>
                    <switch
                      :checked="m.importOn"
                      color="#6ACDBB"
                      style="transform: scale(0.6)"
                      :data-mi="m.mi"
                      @change="onToggleImportTap"
                    />
                  </view>
                  <text class="fs-20 color-sub">{{ m.doseUnit }}{{ m.usageText }}</text>
                  <view class="flex-row flex-wrap m-t-8">
                    <view
                      v-for="c in m.candidates"
                      :key="c.drug_id"
                      class="ai-cand ai-cand--sm"
                      :class="{ 'ai-cand--on': m.selected_drug_id == c.drug_id }"
                      :data-mi="m.mi"
                      :data-drug-id="c.drug_id"
                      @click="onPickCandidateTap"
                    >
                      <text class="fs-18">{{ c.drug_name }}</text>
                    </view>
                  </view>
                </view>
              </view>
              <block v-else>
                <view v-for="m in matchedView" :key="m.mi" class="ai-match-row ai-match-row--card">
                  <view class="flex-row flex-ali-center flex-jus-sp">
                    <text class="fs-26 font-bold ai-match-name">{{ m.ai_name }}</text>
                    <text class="fs-22 color-sub">{{ m.doseUnit }}{{ m.usageText }}</text>
                    <switch
                      :checked="m.importOn"
                      color="#6ACDBB"
                      style="transform: scale(0.65)"
                      :data-mi="m.mi"
                      @change="onToggleImportTap"
                    />
                  </view>
                  <view class="flex-row flex-wrap m-t-8">
                    <view
                      v-for="c in m.candidates"
                      :key="c.drug_id"
                      class="ai-cand ai-cand--sm"
                      :class="{ 'ai-cand--on': m.selected_drug_id == c.drug_id }"
                      :data-mi="m.mi"
                      :data-drug-id="c.drug_id"
                      @click="onPickCandidateTap"
                    >
                      <text class="fs-20">{{ c.drug_name }}</text>
                    </view>
                  </view>
                </view>
              </block>
              <view class="ai-sec-title m-t-16">未对照（{{ unmatchedCount }}）</view>
              <view
                v-for="u in unmatchedView"
                :key="u.ui"
                class="ai-match-row ai-match-row--warn"
              >
                <text class="fs-26">{{ u.ai_name }}</text>
                <text class="ai-link m-t-8" :data-ui="u.ui" @click="onSearchUnmatchedTap">去手动搜索</text>
              </view>
              <view
                v-if="rxPrescriptionName || rxReason || rxBasis || rxDosage || rxDayDosage || lastDurationMs || rxProcessRuleName"
                class="ai-rx-meta m-t-16"
              >
                <view class="ai-desc-row ai-desc-row--full">
                  <text class="ai-desc-label">药方名</text>
                  <text class="ai-desc-val">{{ rxPrescriptionName || '未命名药方' }}</text>
                </view>
                <view class="ai-desc-row">
                  <text class="ai-desc-label">推荐剂数</text>
                  <text class="ai-desc-val">{{ rxDosage || '—' }}剂</text>
                </view>
                <view class="ai-desc-row">
                  <text class="ai-desc-label">每天几次</text>
                  <text class="ai-desc-val">{{ rxDayDosage || '—' }}次</text>
                </view>
                <view v-if="lastDurationMs" class="ai-desc-row">
                  <text class="ai-desc-label">生成耗时</text>
                  <text class="ai-desc-val">{{ durationText }}</text>
                </view>
                <view v-if="rxProcessRuleName" class="ai-desc-row ai-desc-row--full">
                  <text class="ai-desc-label">制剂要求</text>
                  <text class="ai-desc-val">{{ rxProcessRuleText }}</text>
                </view>
                <view v-if="rxReason" class="ai-desc-row ai-desc-row--full">
                  <text class="ai-desc-label">为什么出这个方</text>
                  <text class="ai-desc-val">{{ rxReason }}</text>
                </view>
                <view v-if="rxBasis" class="ai-desc-row ai-desc-row--full">
                  <text class="ai-desc-label">根据哪些</text>
                  <text class="ai-desc-val">{{ rxBasis }}</text>
                </view>
              </view>
            </block>
          </block>
        </scroll-view>
      </view>
      <!-- 二级：抽屉内全屏生成面板（不用第二个 popup） -->
      <view v-else-if="!consentVisible && genModalVisible" class="ai-gen-panel">
        <view class="ai-gen-hd flex-row flex-jus-sp flex-ali-center">
          <text class="fs-30 font-bold">生成处方</text>
          <text class="ai-rx-sub" @click="closeGenModal">返回</text>
        </view>
        <text class="ai-gen-tip">点击文字可编辑，将同步到病历 · {{ typeLabel }}</text>
        <scroll-view scroll-y class="ai-gen-scroll">
          <view class="ai-gen-grid">
            <view
              v-for="row in genPreviewView"
              :key="row.key"
              class="ai-gen-row"
              :class="{ 'ai-gen-row--full': row.key == 'chief_complaint' }"
            >
              <text class="ai-gen-label">{{ row.label }}</text>
              <textarea
                v-if="editingKey == row.key"
                class="ai-gen-textarea"
                :value="row.value"
                :data-key="row.key"
                auto-height
                maxlength="2000"
                @input="onGenFieldInput"
                @blur="onGenFieldBlur"
              />
              <text
                v-else
                class="ai-gen-val"
                :data-key="row.key"
                @click="onStartEditTap"
              >{{ row.value || '（空，点击填写）' }}</text>
            </view>
          </view>
          <!-- 中药：生成前可选委托调剂 + 制剂要求 -->
          <view v-if="isTcmRx" class="ai-entrust-box">
            <view class="flex-row flex-ali-center flex-jus-sp">
              <text class="fs-24">委托调剂</text>
              <switch
                :checked="useEntrustedProcess"
                color="#6ACDBB"
                style="transform: scale(0.75)"
                @change="onEntrustedSwitch"
              />
            </view>
            <view v-if="useEntrustedProcess" class="m-t-12">
              <text class="fs-22 color-sub">制剂要求</text>
              <picker
                mode="selector"
                :range="processRuleNameList"
                :value="processRulePickerIndex"
                @change="onProcessRulePick"
              >
                <view class="ai-process-pick">
                  <text class="fs-24">{{ processRulePickLabel }}</text>
                </view>
              </picker>
            </view>
          </view>
        </scroll-view>
      </view>
      <view class="ai-rx-footer safe-area-inset-bottom">
        <view v-if="consentVisible" class="ai-actions">
          <view class="ai-btn ai-btn--ghost" @click="onConsentCancel">取消</view>
          <view class="ai-btn ai-btn--primary" @click="onConsentAgree">
            {{ consentSubmitting ? '提交中…' : '我已知悉并同意' }}
          </view>
        </view>
        <view v-else-if="!genModalVisible" class="ai-actions">
          <view class="ai-btn ai-btn--ghost" @click="onClose">关闭</view>
          <view class="ai-btn ai-btn--ghost" @click="openGenModal">生成处方</view>
          <view class="ai-btn ai-btn--primary" @click="onConfirmImport">确认导入</view>
        </view>
        <view v-else class="ai-actions">
          <view class="ai-btn ai-btn--ghost" @click="closeGenModal">返回</view>
          <view class="ai-btn ai-btn--primary" @click="onGenerate">
            {{ generating ? '生成中…' : '开始生成' }}
          </view>
        </view>
      </view>
    </view>
  </u-popup>
</template>

<script>
import {
  aiAgreeConsentApi,
  aiFeatureGateApi,
  aiGeneratePrescriptionApi,
  aiListGenerationsApi,
  aiMatchPrescriptionDrugsApi,
  getProcessRuleList,
} from '@/api/reception.js'

const MR_EDIT_FIELDS = [
  { key: 'present_illness', label: '现病史' },
  { key: 'tongue', label: '舌象' },
  { key: 'pulse', label: '脉象' },
  { key: 'tcm_case', label: '中医病案' },
  { key: 'tcm_disease', label: '中医疾病' },
  { key: 'tcm_syndrome', label: '中医证候' },
  { key: 'tcm_method', label: '中医治法' },
  { key: 'diagnosis', label: '临床诊断' },
  { key: 'treatment_advice', label: '治疗意见' },
  { key: 'doctor_order', label: '医嘱' },
  { key: 'allergy_history', label: '过敏史' },
  { key: 'past_history', label: '既往史' },
  { key: 'family_history', label: '家族史' },
  { key: 'epidemic_history', label: '流行病学史' },
  { key: 'personal_history', label: '个人史' },
  { key: 'menstrual_history', label: '月经史' },
  { key: 'marital_history', label: '婚育史' },
  { key: 'physical_exam', label: '体征检查' },
  { key: 'auxiliary_exam', label: '辅助检查' },
]

export default {
  name: 'AiPrescriptionDrawer',
  props: {
    value: { type: Boolean, default: false },
    registerId: { type: [Number, String], default: 0 },
    storeId: { type: [Number, String], default: 0 },
    prescriptionType: { type: [Number, String], default: 0 },
    patientName: { type: String, default: '' },
    patientSex: { type: [Number, String], default: 0 },
    patientAge: { type: [Number, String], default: 0 },
    chiefComplaint: { type: String, default: '' },
    medicalRecord: { type: Object, default: () => ({}) },
  },
  data() {
    return {
      visible: false,
      genModalVisible: false,
      aiChief: '',
      localMr: {},
      editingKey: '',
      histLoading: false,
      generating: false,
      matchLoading: false,
      historyList: [],
      historyView: [],
      historyEmpty: true,
      activeId: 0,
      matchResult: null,
      matchedList: [],
      unmatchedList: [],
      matchedView: [],
      unmatchedView: [],
      matchedCount: 0,
      unmatchedCount: 0,
      conflictView: [],
      rxDosage: 0,
      rxDayDosage: 0,
      rxReason: '',
      rxBasis: '',
      rxPrescriptionName: '',
      lastDurationMs: 0,
      rxProcessRuleId: 0,
      rxChildProcessRuleId: 0,
      rxProcessRuleNoteId: 0,
      rxProcessRuleName: '',
      rxChildProcessRuleName: '',
      rxProcessRuleNote: '',
      useEntrustedProcess: false,
      genProcessRuleId: 0,
      processRuleList: [],
      processRulePickerIndex: 0,
      processRuleLoading: false,
      genPreviewView: [],
      disclaimerText: '',
      consentText: '',
      consentVisible: false,
      consentSubmitting: false,
      importBtnStyle: {
        backgroundColor: '#6ACDBB',
        color: '#fff',
        border: 'none',
        width: '100%',
      },
    }
  },
  computed: {
    sexLabel() {
      const s = Number(this.patientSex)
      if (s === 1) return '男'
      if (s === 2) return '女'
      return '未知'
    },
    typeLabel() {
      const t = Number(this.prescriptionType)
      if (t === 1) return '中药'
      if (t === 2) return '西（中成）药'
      return t ? '类型' + t : '—'
    },
    isTcmRx() {
      return Number(this.prescriptionType) === 1
    },
    /** 毫秒转可读耗时，供模板展示 */
    durationText() {
      return this.formatDurationMs(this.lastDurationMs)
    },
    processRuleNameList() {
      const list = Array.isArray(this.processRuleList) ? this.processRuleList : []
      return list.map((item) => String(item.name || item.title || ('#' + item.id)))
    },
    processRulePickLabel() {
      if (this.genProcessRuleId && this.processRuleList.length) {
        for (let i = 0; i < this.processRuleList.length; i++) {
          if (Number(this.processRuleList[i].id) === Number(this.genProcessRuleId)) {
            return String(this.processRuleList[i].name || this.processRuleList[i].title || ('#' + this.genProcessRuleId))
          }
        }
      }
      return this.processRuleLoading ? '加载中…' : '请选择制剂要求'
    },
    rxProcessRuleText() {
      let text = this.rxProcessRuleName || ''
      if (this.rxChildProcessRuleName) text += ' · ' + this.rxChildProcessRuleName
      if (this.rxProcessRuleNote) text += ' · ' + this.rxProcessRuleNote
      return text
    },
  },
  watch: {
    value: {
      immediate: true,
      handler(v) {
        this.visible = !!v
        if (v) this.bootstrapOpen()
      },
    },
    visible(v) {
      this.$emit('input', v)
      this.$emit('overlay-change', !!v)
    },
  },
  methods: {
    bootstrapOpen() {
      this.aiChief = String(this.chiefComplaint || '').trim()
      this.localMr = Object.assign({}, this.medicalRecord || {})
      this.genModalVisible = false
      this.editingKey = ''
      this.consentVisible = false
      this.consentSubmitting = false
      this.disclaimerText = ''
      this.consentText = ''
      this.useEntrustedProcess = false
      this.genProcessRuleId = 0
      this.processRuleList = []
      this.processRulePickerIndex = 0
      this.resetPreview()
      this.ensureConsentThenBootstrap()
    },
    /**
     * 首次使用 AI 出方：未签署则先展示知情同意
     */
    async ensureConsentThenBootstrap() {
      try {
        const res = await aiFeatureGateApi({ feature_code: 'ai_prescription' })
        const gate = this.unwrapBiz(res) || {}
        this.disclaimerText = String(gate.disclaimer_text || '')
        this.consentText = String(gate.consent_text || '')
        if (gate.consented) {
          this.afterConsentReady()
          return
        }
        this.consentVisible = true
      } catch (e) {
        const msg = (e && e.message) || (e && e.msg) || '无法校验 AI 使用规范'
        uni.showToast({ title: String(msg), icon: 'none' })
        this.onClose()
      }
    },
    afterConsentReady() {
      this.consentVisible = false
      this.loadHistory().then(() => {
        if (this.historyList.length) {
          this.selectHistoryById(Number(this.historyList[0].id))
        } else {
          this.openGenModal()
        }
      })
    },
    onConsentCancel() {
      this.onClose()
    },
    async onConsentAgree() {
      if (this.consentSubmitting) return
      this.consentSubmitting = true
      try {
        await aiAgreeConsentApi({
          feature_code: 'ai_prescription',
          store_id: Number(this.storeId || 0),
        })
        uni.showToast({ title: '已记录知情同意', icon: 'none' })
        this.afterConsentReady()
      } catch (e) {
        const msg = (e && e.message) || (e && e.msg) || '签署失败'
        uni.showToast({ title: String(msg), icon: 'none' })
      } finally {
        this.consentSubmitting = false
      }
    },
    resetPreview() {
      this.matchResult = null
      this.matchedList = []
      this.unmatchedList = []
      this.conflictView = []
      this.rxDosage = 0
      this.rxDayDosage = 0
      this.rxReason = ''
      this.rxBasis = ''
      this.rxPrescriptionName = ''
      this.lastDurationMs = 0
      this.rxProcessRuleId = 0
      this.rxChildProcessRuleId = 0
      this.rxProcessRuleNoteId = 0
      this.rxProcessRuleName = ''
      this.rxChildProcessRuleName = ''
      this.rxProcessRuleNote = ''
      this.activeId = 0
      this.rebuildMatchView()
    },
    onClose() {
      this.visible = false
      this.genModalVisible = false
    },
    /**
     * Laravel jok 业务在 result；兼容 Yii data / 已解包数组
     */
    unwrapBiz(res) {
      if (res == null) return null
      if (res.result != null) return res.result
      if (res.data != null) return res.data
      return res
    },
    formatTime(ts) {
      // 已是格式化字符串则直接展示（查询器可能一级格式化）
      if (typeof ts === 'string' && ts && Number.isNaN(Number(ts))) {
        return ts.length > 16 ? ts.slice(5, 16) : ts
      }
      const n = Number(ts || 0)
      if (!n) return '—'
      const d = new Date(n * 1000)
      const pad = (x) => (x < 10 ? '0' + x : '' + x)
      return pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes())
    },
    /** 毫秒转可读耗时（850ms / 1.2s） */
    formatDurationMs(ms) {
      const n = Number(ms || 0)
      if (!n) return ''
      if (n < 1000) return Math.round(n) + 'ms'
      const sec = n / 1000
      return sec >= 10 ? Math.round(sec) + 's' : sec.toFixed(1) + 's'
    },
    rebuildHistoryView() {
      const list = Array.isArray(this.historyList) ? this.historyList : []
      this.historyEmpty = list.length === 0
      this.historyView = list.map((row) => {
        const id = Number(row.id || 0)
        const cnt = Number(row.item_count || 0)
        const pt = Number(row.prescription_type || 0)
        const name = String(row.name || row.prescription_name || '').trim()
        let typeLabel = String(row.prescription_type_label || '').trim()
        if (!typeLabel) {
          if (pt === 1) typeLabel = '中药'
          else if (pt === 2) typeLabel = '西药（中成药）'
          else typeLabel = pt ? '类型' + pt : '—'
        }
        return {
          id: id,
          nameText: name || ('方案 #' + id),
          typeText: typeLabel + ' · ' + cnt + '味 · ' + this.formatTime(row.created_at) + (row.duration_ms ? (' · ' + this.formatDurationMs(row.duration_ms)) : ''),
          prescription_type: pt,
        }
      })
    },
    rebuildMatchView() {
      const matched = Array.isArray(this.matchedList) ? this.matchedList : []
      this.matchedCount = matched.length
      this.matchedView = matched.map((m, mi) => {
        const cands = Array.isArray(m.candidates) ? m.candidates : []
        return {
          mi: mi,
          ai_name: m.ai_name || '',
          doseUnit: String(m.dose || '') + String(m.unit || ''),
          usageText: m.usage ? ('·' + m.usage) : '',
          selected_drug_id: Number(m.selected_drug_id || 0),
          importOn: m._import !== false,
          candidates: cands.map((c) => ({
            drug_id: Number(c.drug_id || 0),
            drug_name: c.drug_name || '',
          })),
        }
      })
      const unmatched = Array.isArray(this.unmatchedList) ? this.unmatchedList : []
      this.unmatchedCount = unmatched.length
      this.unmatchedView = unmatched.map((u, ui) => ({
        ui: ui,
        ai_name: u.ai_name || '',
        tip: String(u.dose || '') + String(u.unit || '') + ' · 未匹配',
      }))
    },
    rebuildGenPreview() {
      const mr = this.localMr || {}
      const rows = [
        {
          key: 'chief_complaint',
          label: '主诉',
          value: String(this.aiChief || mr.chief_complaint || '').trim(),
        },
      ]
      for (let i = 0; i < MR_EDIT_FIELDS.length; i++) {
        const f = MR_EDIT_FIELDS[i]
        const val = String(mr[f.key] || '').trim()
        if (val) rows.push({ key: f.key, label: f.label, value: val })
      }
      this.genPreviewView = rows
    },
    applyRxMeta(data) {
      this.rxDosage = Number((data && data.dosage) || 0)
      this.rxDayDosage = Number((data && data.day_dosage) || 0)
      this.rxReason = String((data && data.reason) || '').trim()
      this.rxBasis = String((data && data.basis) || '').trim()
      this.rxPrescriptionName = String((data && data.prescription_name) || '').trim()
      if (data && data.duration_ms != null) {
        this.lastDurationMs = Number(data.duration_ms || 0)
      }
      this.rxProcessRuleId = Number((data && data.process_rule_id) || 0)
      this.rxChildProcessRuleId = Number((data && data.child_process_rule_id) || 0)
      this.rxProcessRuleNoteId = Number((data && data.process_rule_note_id) || 0)
      this.rxProcessRuleName = String((data && data.process_rule_name) || '').trim()
      this.rxChildProcessRuleName = String((data && data.child_process_rule_name) || '').trim()
      this.rxProcessRuleNote = String((data && data.process_rule_note) || '').trim()
    },
    /** 加载制剂要求（pid=0），供中药委托调剂选择 */
    async loadTopProcessRules() {
      if (!this.isTcmRx) return
      this.processRuleLoading = true
      try {
        const res = await getProcessRuleList({ pid: 0 })
        const list = this.unwrapBiz(res)
        this.processRuleList = Array.isArray(list) ? list : []
      } catch (e) {
        this.processRuleList = []
      } finally {
        this.processRuleLoading = false
      }
    },
    onEntrustedSwitch(e) {
      const checked = !!(e && e.detail && e.detail.value)
      this.useEntrustedProcess = checked
      if (checked && !this.processRuleList.length) {
        this.loadTopProcessRules()
      }
      if (!checked) {
        this.genProcessRuleId = 0
        this.processRulePickerIndex = 0
      }
    },
    onProcessRulePick(e) {
      const idx = Number((e && e.detail && e.detail.value) || 0)
      this.processRulePickerIndex = idx
      const item = this.processRuleList[idx]
      this.genProcessRuleId = item ? Number(item.id || 0) : 0
    },
    applyConflict(conflict) {
      const msgs = (conflict && conflict.messages) || []
      this.conflictView = (Array.isArray(msgs) ? msgs : [])
        .map((m) => String(m || '').trim())
        .filter((m) => !!m)
    },
    applyMatch(match) {
      this.matchResult = match || null
      const matched = (match && match.matched) || []
      this.matchedList = (Array.isArray(matched) ? matched : []).map((m) =>
        Object.assign({}, m, { _import: true }),
      )
      this.unmatchedList = Array.isArray(match && match.unmatched) ? match.unmatched : []
      this.rebuildMatchView()
    },
    async loadHistory() {
      this.histLoading = true
      try {
        const res = await aiListGenerationsApi({
          register_id: Number(this.registerId),
          scene: 'prescription',
          limit: 30,
        })
        const list = this.unwrapBiz(res)
        this.historyList = Array.isArray(list) ? list : []
        this.rebuildHistoryView()
      } catch (e) {
        this.historyList = []
        this.rebuildHistoryView()
        const msg = (e && e.message) || (e && e.msg) || ''
        if (msg) uni.showToast({ title: String(msg), icon: 'none' })
      } finally {
        this.histLoading = false
      }
    },
    openGenModal() {
      if (!Number(this.prescriptionType || 0)) {
        uni.showToast({ title: '请先选择处方类型', icon: 'none' })
        return
      }
      this.editingKey = ''
      this.rebuildGenPreview()
      if (this.isTcmRx && this.useEntrustedProcess && !this.processRuleList.length) {
        this.loadTopProcessRules()
      }
      this.$nextTick(() => {
        this.genModalVisible = true
      })
    },
    closeGenModal() {
      this.genModalVisible = false
      this.editingKey = ''
    },
    onStartEditTap(e) {
      const key = e && e.currentTarget && e.currentTarget.dataset
        ? e.currentTarget.dataset.key
        : ''
      if (!key) return
      this.editingKey = key
    },
    onGenFieldInput(e) {
      const key = e && e.currentTarget && e.currentTarget.dataset
        ? e.currentTarget.dataset.key
        : this.editingKey
      const val = (e && e.detail && e.detail.value) || ''
      if (!key) return
      if (key === 'chief_complaint') {
        this.aiChief = val
        this.localMr = Object.assign({}, this.localMr, { chief_complaint: val })
      } else {
        const next = Object.assign({}, this.localMr)
        next[key] = val
        this.localMr = next
      }
      // 同步到父页病历
      const payload = {}
      payload[key] = val
      this.$emit('sync-medical-record', payload)
      // 更新预览行展示值
      for (let i = 0; i < this.genPreviewView.length; i++) {
        if (this.genPreviewView[i].key === key) {
          this.$set(this.genPreviewView[i], 'value', val)
          break
        }
      }
    },
    onGenFieldBlur() {
      this.editingKey = ''
    },
    async onGenerate() {
      if (this.generating) return
      const chief = String(this.aiChief || '').trim()
      if (!chief) {
        uni.showToast({ title: '请填写主诉后再生成', icon: 'none' })
        return
      }
      const type = Number(this.prescriptionType || 0)
      if (!type) {
        uni.showToast({ title: '请先选择处方类型', icon: 'none' })
        return
      }
      if (this.isTcmRx && this.useEntrustedProcess && !this.genProcessRuleId) {
        uni.showToast({ title: '请选择制剂要求', icon: 'none' })
        return
      }
      // 先关二级弹窗，回主抽屉等待生成
      this.closeGenModal()
      this.generating = true
      try {
        const req = {
          register_id: Number(this.registerId),
          store_id: Number(this.storeId || 0) || undefined,
          prescription_type: type,
          chief_complaint: chief,
          medical_record: Object.assign({}, this.localMr || {}, {
            chief_complaint: chief,
          }),
        }
        if (this.isTcmRx && this.useEntrustedProcess && this.genProcessRuleId) {
          req.use_entrusted_process = 1
          req.process_rule_id = this.genProcessRuleId
        }
        const res = await aiGeneratePrescriptionApi(req)
        const data = this.unwrapBiz(res) || {}
        const durationText = this.formatDurationMs(data.duration_ms)
        // 业务软失败：HTTP 成功但 ok=false，toast 警告而非失败
        if (data.ok === false) {
          this.matchResult = null
          this.matchedList = []
          this.unmatchedList = []
          this.conflictView = []
          this.rebuildMatchView()
          this.applyRxMeta({ duration_ms: data.duration_ms })
          if (data.generation_id) {
            this.activeId = Number(data.generation_id)
            await this.loadHistory()
          }
          uni.showToast({ title: String(data.message || '生成未成功'), icon: 'none' })
          return
        }
        await this.loadHistory()
        this.activeId = Number(data.generation_id || 0)
        this.applyMatch(data.match || null)
        this.applyConflict(data.conflict || null)
        this.applyRxMeta(data)
        uni.showToast({
          title: durationText ? ('已生成（耗时 ' + durationText + '）') : '已生成',
          icon: 'success',
        })
      } catch (e) {
        const msg = (e && e.message) || (e && e.msg) || '生成失败'
        uni.showToast({ title: String(msg), icon: 'none' })
      } finally {
        this.generating = false
      }
    },
    onSelectHistoryTap(e) {
      const raw = e && e.currentTarget && e.currentTarget.dataset
        ? e.currentTarget.dataset.id
        : 0
      const id = Number(raw || 0)
      if (!id) return
      this.selectHistoryById(id)
    },
    async selectHistoryById(id) {
      this.activeId = id
      this.matchLoading = true
      try {
        let pt = Number(this.prescriptionType || 0)
        let histName = ''
        let histRow = null
        for (let i = 0; i < this.historyList.length; i++) {
          if (Number(this.historyList[i].id) === id) {
            histRow = this.historyList[i]
            pt = Number(histRow.prescription_type || pt)
            histName = String(
              histRow.name || histRow.prescription_name || '',
            ).trim()
            break
          }
        }
        const res = await aiMatchPrescriptionDrugsApi({
          generation_id: id,
          register_id: Number(this.registerId),
          store_id: Number(this.storeId || 0) || undefined,
          prescription_type: pt,
        })
        const data = this.unwrapBiz(res) || {}
        this.applyMatch(data)
        this.applyConflict(data.conflict || null)
        this.applyRxMeta({
          dosage: data.dosage,
          day_dosage: data.day_dosage,
          reason: data.reason,
          basis: data.basis,
          duration_ms: histRow ? histRow.duration_ms : 0,
          process_rule_id: data.process_rule_id,
          child_process_rule_id: data.child_process_rule_id,
          process_rule_note_id: data.process_rule_note_id,
          process_rule_name: data.process_rule_name,
          child_process_rule_name: data.child_process_rule_name,
          process_rule_note: data.process_rule_note,
          prescription_name: data.prescription_name || histName,
        })
      } catch (e) {
        this.matchResult = null
        this.matchedList = []
        this.unmatchedList = []
        this.conflictView = []
        this.rebuildMatchView()
        const msg = (e && e.message) || (e && e.msg) || '对照失败'
        uni.showToast({ title: String(msg), icon: 'none' })
      } finally {
        this.matchLoading = false
      }
    },
    onPickCandidateTap(e) {
      const ds = (e && e.currentTarget && e.currentTarget.dataset) || {}
      const mi = Number(ds.mi || 0)
      const drugId = Number(ds.drugId || ds['drug-id'] || 0)
      if (!this.matchedList[mi]) return
      this.$set(this.matchedList[mi], 'selected_drug_id', drugId)
      this.rebuildMatchView()
    },
    onToggleImportTap(e) {
      const ds = (e && e.currentTarget && e.currentTarget.dataset) || {}
      const mi = Number(ds.mi || 0)
      const checked = !!(e && e.detail && e.detail.value)
      if (!this.matchedList[mi]) return
      this.$set(this.matchedList[mi], '_import', checked)
      this.rebuildMatchView()
    },
    onSearchUnmatchedTap(e) {
      const ds = (e && e.currentTarget && e.currentTarget.dataset) || {}
      const ui = Number(ds.ui || 0)
      const row = this.unmatchedList[ui]
      this.$emit('search-drug', (row && row.ai_name) || '')
      this.visible = false
    },
    onConfirmImport() {
      const drugs = []
      for (let i = 0; i < this.matchedList.length; i++) {
        const m = this.matchedList[i]
        if (!m || m._import === false) continue
        const sid = Number(m.selected_drug_id || 0)
        if (!sid) continue
        const cands = m.candidates || []
        let picked = null
        for (let j = 0; j < cands.length; j++) {
          if (Number(cands[j].drug_id) === sid) {
            picked = cands[j]
            break
          }
        }
        if (!picked) continue
        drugs.push({
          candidate: picked,
          dose: m.dose || '',
          unit: m.unit || '',
          usage: m.usage || '',
          ai_name: m.ai_name || '',
        })
      }
      if (!drugs.length) {
        uni.showToast({ title: '请先勾选已对照药品', icon: 'none' })
        return
      }
      this.$emit('import', {
        rows: drugs,
        dosage: this.rxDosage || undefined,
        day_dosage: this.rxDayDosage || undefined,
        rule_type: this.rxProcessRuleId > 0 ? 2 : undefined,
        process_rule_id: this.rxProcessRuleId > 0 ? this.rxProcessRuleId : undefined,
        child_process_rule_id: this.rxChildProcessRuleId > 0 ? this.rxChildProcessRuleId : undefined,
        process_rule_note_id: this.rxProcessRuleNoteId > 0 ? this.rxProcessRuleNoteId : undefined,
      })
      this.visible = false
    },
  },
}
</script>

<style scoped lang="scss">
.ai-rx-drawer {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #f7f8fa;
  position: relative;
  overflow: hidden;
}
.ai-gen-panel {
  flex: 1;
  height: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  background: #fff;
}
.ai-rx-title-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24rpx 32rpx;
  background: #fff;
}
.ai-rx-title {
  font-size: 32rpx;
  font-weight: bold;
  color: #333;
}
.ai-rx-sub {
  font-size: 24rpx;
  color: #6acdbb;
}
.ai-rx-meta {
  margin: 0 0 12rpx;
  background: #fff;
  border-radius: 12rpx;
  border: 1px solid #eee;
  overflow: hidden;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
.ai-desc-row {
  width: 50%;
  display: flex;
  flex-direction: column;
  padding: 12rpx 16rpx;
  box-sizing: border-box;
  border-bottom: 1px solid #f0f0f0;
  border-right: 1px solid #f0f0f0;
}
.ai-desc-row--full {
  width: 100%;
  border-right: none;
}
.ai-desc-label {
  font-size: 20rpx;
  color: #999;
  margin-bottom: 4rpx;
}
.ai-desc-val {
  font-size: 24rpx;
  color: #2a2e35;
  line-height: 1.45;
  white-space: pre-wrap;
}
.ai-conflict {
  margin: 0 24rpx 12rpx;
  padding: 16rpx;
  background: #fff7e6;
  border: 1px solid #ffe0b2;
  border-radius: 8rpx;
}
.ai-conflict-title {
  display: block;
  font-size: 26rpx;
  font-weight: bold;
  color: #ad6800;
  margin-bottom: 8rpx;
}
.ai-conflict-line {
  font-size: 22rpx;
  color: #874d00;
  line-height: 1.5;
}
.ai-rx-body {
  flex: 1;
  /* 微信 scroll-view 在 flex 下需 height:0 才能吃满剩余高度 */
  height: 0;
  min-height: 0;
  overflow: hidden;
}
.ai-rx-hist {
  width: 220rpx;
  background: #fff;
  border-right: 1px solid #eee;
  height: 100%;
}
.ai-rx-match {
  flex: 1;
  height: 100%;
  padding: 16rpx 20rpx 24rpx;
  box-sizing: border-box;
}
.ai-rx-dose-row {
  margin-top: 10rpx;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
.ai-rx-dose-item {
  margin-right: 32rpx;
  margin-bottom: 4rpx;
  font-size: 24rpx;
  color: #666;
}
.ai-rx-dose-val {
  font-weight: bold;
  color: #2a2e35;
}
.ai-hist-hd {
  padding: 16rpx;
  border-bottom: 1px solid #f0f0f0;
}
.ai-hist-item {
  padding: 16rpx;
  border-bottom: 1px solid #f0f0f0;
  display: flex;
  flex-direction: column;
}
.ai-hist-item--active {
  background: #e8f8f4;
}
.ai-hist-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.ai-sec-title {
  font-size: 24rpx;
  font-weight: bold;
  color: #666;
  margin-bottom: 12rpx;
}
.ai-tcm-grid {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  margin: 0 -6rpx;
}
.ai-tcm-cell {
  width: calc(50% - 12rpx);
  margin: 0 6rpx 12rpx;
  padding: 12rpx;
  background: #fff;
  border-radius: 10rpx;
  box-sizing: border-box;
}
.ai-match-row {
  padding: 12rpx 0;
  border-bottom: 1px solid #f0f0f0;
}
.ai-match-row--card {
  margin-bottom: 12rpx;
  padding: 16rpx;
  background: #fff;
  border-radius: 12rpx;
  border-bottom: none;
}
.ai-match-row--warn {
  margin-bottom: 12rpx;
  padding: 16rpx;
  background: #fff7e6;
  border-radius: 12rpx;
  border-bottom: none;
}
.ai-match-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-right: 8rpx;
}
.ai-cand {
  border: 1px solid #ddd;
  border-radius: 8rpx;
  padding: 8rpx 12rpx;
  margin-right: 12rpx;
  margin-bottom: 8rpx;
}
.ai-cand--sm {
  padding: 4rpx 10rpx;
  margin-right: 8rpx;
  margin-bottom: 8rpx;
}
.ai-cand--on {
  border-color: #6acdbb;
  background: #e8f8f4;
}
.ai-link {
  color: #6acdbb;
  font-size: 24rpx;
}
.ai-rx-footer {
  padding: 16rpx 24rpx 24rpx;
  background: #fff;
}
.ai-disclaimer {
  margin: 0 24rpx 12rpx;
  padding: 16rpx 20rpx;
  border-radius: 12rpx;
  border: 1px solid rgba(245, 158, 11, 0.45);
  background: rgba(245, 158, 11, 0.12);
}
.ai-disclaimer-title {
  display: block;
  font-size: 24rpx;
  font-weight: 600;
  color: #b45309;
  margin-bottom: 6rpx;
}
.ai-disclaimer-body {
  display: block;
  font-size: 22rpx;
  line-height: 1.55;
  color: #78716c;
  white-space: pre-wrap;
}
.ai-consent-panel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: 16rpx 32rpx;
}
.ai-consent-scroll {
  flex: 1;
  height: 0;
  margin-top: 16rpx;
  padding: 16rpx;
  background: #f7f8fa;
  border-radius: 12rpx;
  box-sizing: border-box;
}
.ai-consent-body {
  font-size: 26rpx;
  line-height: 1.65;
  color: #2a2e35;
  white-space: pre-wrap;
}
.ai-actions {
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  gap: 16rpx;
}
.ai-btn {
  padding: 16rpx 28rpx;
  border-radius: 40rpx;
  font-size: 26rpx;
}
.ai-btn--ghost {
  background: #f5f5f5;
  color: #666;
}
.ai-btn--primary {
  background: #6acdbb;
  color: #fff;
}
.ai-btn--block {
  display: block;
  text-align: center;
}
.ai-gen-hd {
  padding: 16rpx 32rpx 8rpx;
}
.ai-gen-tip {
  padding: 0 32rpx 12rpx;
  font-size: 22rpx;
  color: #999;
}
.ai-gen-scroll {
  flex: 1;
  height: 0;
  min-height: 0;
  padding: 0 24rpx 16rpx;
  box-sizing: border-box;
  background: #f7f8fa;
}
.ai-gen-grid {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  margin: 0 -6rpx;
}
.ai-gen-row {
  width: calc(50% - 12rpx);
  margin: 0 6rpx 8rpx;
  padding: 12rpx 16rpx;
  background: #f7f8fa;
  border-radius: 10rpx;
  box-sizing: border-box;
}
.ai-gen-row--full {
  width: calc(100% - 12rpx);
}
.ai-gen-label {
  display: block;
  font-size: 20rpx;
  color: #999;
  margin-bottom: 4rpx;
}
.ai-gen-val {
  display: block;
  font-size: 24rpx;
  color: #2a2e35;
  line-height: 1.5;
  white-space: pre-wrap;
}
.ai-gen-textarea {
  width: 100%;
  min-height: 80rpx;
  font-size: 24rpx;
  color: #2a2e35;
  background: #fff;
  padding: 8rpx;
  box-sizing: border-box;
  border-radius: 6rpx;
}
.ai-entrust-box {
  margin: 16rpx 6rpx 0;
  padding: 16rpx 20rpx;
  background: #fff;
  border-radius: 12rpx;
  border: 1px solid #eee;
}
.ai-process-pick {
  margin-top: 8rpx;
  padding: 16rpx 20rpx;
  background: #f7f8fa;
  border-radius: 8rpx;
}
.m-t-12 {
  margin-top: 12rpx;
}
.color-sub {
  color: #999;
}
.p-24 {
  padding: 24rpx;
}
.p-32 {
  padding: 32rpx;
}
.m-t-8 {
  margin-top: 8rpx;
}
.m-t-16 {
  margin-top: 16rpx;
}
.fs-18 {
  font-size: 18rpx;
}
.fs-20 {
  font-size: 20rpx;
}
.fs-22 {
  font-size: 22rpx;
}
.fs-24 {
  font-size: 24rpx;
}
.fs-26 {
  font-size: 26rpx;
}
.fs-30 {
  font-size: 30rpx;
}
.font-bold {
  font-weight: bold;
}
.flex-row {
  display: flex;
  flex-direction: row;
}
.flex-wrap {
  flex-wrap: wrap;
}
.flex-ali-center {
  align-items: center;
}
.flex-jus-sp {
  justify-content: space-between;
}
</style>
