<script lang="ts" setup>
/**
 * 添加医生排班弹窗（右键菜单快捷入口）
 * 流程：右键日历日期 →「添加医生排班…」→ 本弹窗选医生（头像标签多选）→
 * 选排班方式（套用班次模板 / 自定义时段）→ 提交 save-days 批量下发。
 * 与底部操作条的区别：操作条依赖右栏批量集合，这里是一次性向导式选择，
 * 适合「临时给某几天加几位医生的班」的快捷场景，不污染批量集合状态。
 * 医生专属模板：勾选「优先按医生专属模板」时，有专属模板的医生按自己的
 * 时段/号源写入（医生标签带「专」标），无专属的回落所选通用模板兜底。
 */
import type { ScheduleSlot, ShiftTemplate } from '../../api/schedule';

import { computed, ref } from 'vue';

import { useVbenModal } from '@vben/common-ui';

import { Avatar, Button, Empty, message, Segmented, Switch } from 'ant-design-vue';
import dayjs from 'dayjs';

import { Icon } from '#/components/icon';

import { saveScheduleDaysApi } from '../../api/schedule';
import SlotRowsEditor from './SlotRowsEditor.vue';

const emit = defineEmits<{ saved: [] }>();

/** 打开时由父组件 setData 传入的上下文 */
const storeId = ref(0);
const dates = ref<string[]>([]);
const doctors = ref<Array<{ avatar?: string; id: number; name: string }>>([]);
const templates = ref<ShiftTemplate[]>([]);

/** 已勾选医生集合 */
const selectedDoctorIds = ref<number[]>([]);
/** 排班方式：template=套用模板 custom=自定义时段 */
const mode = ref<'custom' | 'template'>('template');
/** 套用模板模式下选中的通用模板（优先专属时作为无专属医生的兜底，可不选） */
const pickedTemplateId = ref<number | undefined>();
/** 自定义时段模式的时段集合 */
const customSlots = ref<ScheduleSlot[]>([]);
/** 优先按医生专属模板写入（仅套用模板模式生效） */
const preferPersonal = ref(true);

/** 店级通用模板（模板卡片列表数据源；专属模板不能被套给任意医生，不进列表） */
const generalTemplates = computed(() =>
  templates.value.filter((t) => Number(t.doctor_id || 0) === 0),
);

/** 医生ID → 专属模板（判断「专」标与提交校验用） */
const personalTemplateMap = computed(() => {
  const map = new Map<number, ShiftTemplate>();
  for (const t of templates.value) {
    const did = Number(t.doctor_id || 0);
    if (did > 0) map.set(did, t);
  }
  return map;
});

/** 已选医生中有专属模板的人数（提示文案用） */
const selectedPersonalCount = computed(
  () => selectedDoctorIds.value.filter((id) => personalTemplateMap.value.has(id)).length,
);

/** 已选医生中无专属模板的人数（优先专属时这些人需要通用兜底） */
const selectedWithoutPersonalCount = computed(
  () => selectedDoctorIds.value.length - selectedPersonalCount.value,
);

/** 日期 chips：最多铺 8 个，其余折叠成 +N（右键选区最多 62 天，全铺会撑爆头部） */
const DATE_SHOW_LIMIT = 8;
const visibleDates = computed(() => dates.value.slice(0, DATE_SHOW_LIMIT));
const hiddenDateCount = computed(() => Math.max(0, dates.value.length - DATE_SHOW_LIMIT));

/** 单击医生标签：切换勾选 */
function toggleDoctor(id: number) {
  const idx = selectedDoctorIds.value.indexOf(id);
  if (idx === -1) {
    selectedDoctorIds.value.push(id);
  } else {
    selectedDoctorIds.value.splice(idx, 1);
  }
}

/** 全选/清空医生 */
function selectAll() {
  selectedDoctorIds.value = doctors.value.map((d) => d.id);
}
function clearAll() {
  selectedDoctorIds.value = [];
}

/** 模板时段摘要（如「上午20 / 下午15」） */
function templateSummary(tpl: ShiftTemplate): string {
  return (tpl.slots || [])
    .map((s) => `${s.slot_name || `${s.start_time}-${s.end_time}`}${s.capacity}`)
    .join(' / ');
}

/** 医生「专」标悬浮说明（专属模板名 + 时段摘要） */
function personalTitle(docId: number): string {
  const tpl = personalTemplateMap.value.get(docId);
  return tpl ? `专属模板：${tpl.name}（${templateSummary(tpl)}）` : '';
}

/** 提交：轻校验后调 save-days（强校验含已约保护在服务端，冲突会点名医生+日期） */
async function handleSubmit() {
  if (selectedDoctorIds.value.length === 0) {
    message.warning('请至少选择一位医生');
    return;
  }
  const extra: Record<string, any> = {};
  if (mode.value === 'template') {
    if (preferPersonal.value) {
      // 优先专属：没有专属模板的医生必须有通用兜底，否则后端会点名拒绝
      if (selectedWithoutPersonalCount.value > 0 && !pickedTemplateId.value) {
        message.warning(
          `有 ${selectedWithoutPersonalCount.value} 位已选医生没有专属模板，请选择一个通用班次模板兜底`,
        );
        return;
      }
      extra.prefer_doctor_template = 1;
      extra.template_id = pickedTemplateId.value || 0;
    } else {
      if (!pickedTemplateId.value) {
        message.warning('请选择一个班次模板');
        return;
      }
      extra.template_id = pickedTemplateId.value;
    }
  } else {
    if (customSlots.value.length === 0) {
      message.warning('请至少添加一个时段');
      return;
    }
    for (const slot of customSlots.value) {
      if (!slot.start_time || !slot.end_time) {
        message.warning('请补全各时段的开始/结束时间');
        return;
      }
      if (!slot.capacity || slot.capacity < 1) {
        message.warning('各时段号源数量至少为 1');
        return;
      }
    }
    extra.slots = customSlots.value;
  }
  ModalApi.lock();
  try {
    await saveScheduleDaysApi({
      doctor_ids: selectedDoctorIds.value,
      store_id: storeId.value,
      dates: dates.value,
      action: mode.value === 'template' ? 'apply_template' : 'custom',
      ...extra,
    });
    message.success(
      `已为 ${selectedDoctorIds.value.length} 位医生排好 ${dates.value.length} 天的班`,
    );
    emit('saved');
    ModalApi.close();
  } catch {
    // 错误提示由 requestClient 统一弹出（如已约保护拦截）
  } finally {
    ModalApi.unlock();
  }
}

const [Modal, ModalApi] = useVbenModal({
  class: 'w-[92%] md:w-[640px]',
  confirmText: '保存排班',
  onConfirm: handleSubmit,
  onOpenChange(isOpen) {
    if (!isOpen) return;
    const data = ModalApi.getData() || {};
    storeId.value = Number(data.storeId || 0);
    dates.value = Array.isArray(data.dates) ? [...data.dates].sort() : [];
    doctors.value = Array.isArray(data.doctors) ? data.doctors : [];
    templates.value = Array.isArray(data.templates) ? data.templates : [];
    // 每次打开重置选择：向导式一次性操作，不继承上次状态
    selectedDoctorIds.value = [];
    mode.value = 'template';
    preferPersonal.value = true;
    pickedTemplateId.value =
      generalTemplates.value.length === 1 ? generalTemplates.value[0]!.id : undefined;
    if (customSlots.value.length === 0) {
      customSlots.value = [
        { slot_name: '', start_time: '08:00', end_time: '12:00', capacity: 10 },
      ];
    }
  },
});

defineExpose({ ModalApi });
</script>

<template>
  <Modal title="添加医生排班">
    <div class="adddoc">
      <!-- 目标日期 -->
      <div class="adddoc__section">
        <div class="adddoc__label">目标日期（{{ dates.length }} 天）</div>
        <div class="adddoc__dates">
          <span v-for="d in visibleDates" :key="d" class="adddoc__date-chip">
            {{ dayjs(d).format('M月D日') }}
          </span>
          <span v-if="hiddenDateCount > 0" class="adddoc__date-chip adddoc__date-chip--more">
            +{{ hiddenDateCount }}
          </span>
        </div>
      </div>

      <!-- 选医生：头像 + 姓名标签网格多选 -->
      <div class="adddoc__section">
        <div class="adddoc__label-row">
          <span class="adddoc__label">选择医生（已选 {{ selectedDoctorIds.length }} 人）</span>
          <span class="adddoc__label-tools">
            <Button size="small" type="link" @click="selectAll">全选</Button>
            <Button
              :disabled="selectedDoctorIds.length === 0"
              size="small"
              type="link"
              @click="clearAll"
            >
              清空
            </Button>
          </span>
        </div>
        <div class="adddoc__doctors">
          <button
            v-for="doc in doctors"
            :key="doc.id"
            class="adddoc__doc"
            :class="{ 'adddoc__doc--active': selectedDoctorIds.includes(doc.id) }"
            type="button"
            @click="toggleDoctor(doc.id)"
          >
            <Avatar :size="22" :src="doc.avatar || undefined" class="adddoc__doc-avatar">
              {{ doc.name.slice(0, 1) }}
            </Avatar>
            <span class="adddoc__doc-name">{{ doc.name }}</span>
            <!-- 「专」标：该医生有专属模板，优先专属时按其自己的时段/号源写入 -->
            <span
              v-if="personalTemplateMap.has(doc.id)"
              class="adddoc__doc-personal"
              :title="personalTitle(doc.id)"
            >
              专
            </span>
            <Icon
              v-if="selectedDoctorIds.includes(doc.id)"
              class="adddoc__doc-check"
              icon="lucide:check"
            />
          </button>
          <Empty
            v-if="doctors.length === 0"
            :image-style="{ height: '48px' }"
            description="该门店暂无医生"
          />
        </div>
      </div>

      <!-- 选排班方式 -->
      <div class="adddoc__section">
        <div class="adddoc__label">排班方式</div>
        <Segmented
          v-model:value="mode"
          :options="[
            { label: '套用班次模板', value: 'template' },
            { label: '自定义时段', value: 'custom' },
          ]"
        />
        <!-- 模板模式：优先专属开关 + 通用模板单选卡片（专属模板不进列表，自动按医生生效） -->
        <div v-if="mode === 'template'" class="adddoc__templates">
          <div class="adddoc__personal-row">
            <Switch v-model:checked="preferPersonal" size="small" />
            <span class="adddoc__personal-label">优先按医生专属模板写入</span>
            <span v-if="preferPersonal && selectedDoctorIds.length > 0" class="adddoc__personal-hint">
              已选 {{ selectedPersonalCount }} 人有专属模板{{
                selectedWithoutPersonalCount > 0
                  ? `，${selectedWithoutPersonalCount} 人将使用下方通用模板`
                  : '，无需选通用模板'
              }}
            </span>
          </div>
          <button
            v-for="tpl in generalTemplates"
            :key="tpl.id"
            class="adddoc__tpl"
            :class="{ 'adddoc__tpl--active': pickedTemplateId === tpl.id }"
            type="button"
            @click="pickedTemplateId = pickedTemplateId === tpl.id ? undefined : tpl.id"
          >
            <span class="adddoc__tpl-name">{{ tpl.name }}</span>
            <span class="adddoc__tpl-summary">{{ templateSummary(tpl) }}</span>
            <Icon
              v-if="pickedTemplateId === tpl.id"
              class="adddoc__tpl-check"
              icon="lucide:check-circle-2"
            />
          </button>
          <Empty
            v-if="generalTemplates.length === 0"
            :image-style="{ height: '48px' }"
            description="暂无启用中的通用班次模板，可切换为自定义时段"
          />
        </div>
        <!-- 自定义时段编辑器 -->
        <div v-else class="adddoc__custom">
          <SlotRowsEditor v-model="customSlots" />
        </div>
      </div>
    </div>
  </Modal>
</template>

<style scoped>
.adddoc {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.adddoc__section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.adddoc__label {
  font-size: 13px;
  font-weight: 600;
  color: hsl(var(--foreground));
}

.adddoc__label-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.adddoc__label-tools {
  display: inline-flex;
  gap: 2px;
}

.adddoc__dates {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.adddoc__date-chip {
  padding: 1px 8px;
  font-size: 12px;
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.1);
  border-radius: 6px;
}

.adddoc__date-chip--more {
  color: hsl(var(--muted-foreground));
  background: hsl(var(--muted) / 0.4);
}

/* 医生标签网格：自适应列宽，限高滚动防医生多时弹窗过长 */
.adddoc__doctors {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(150px, 100%), 1fr));
  gap: 6px;
  max-height: 200px;
  padding: 2px;
  overflow-y: auto;
}

.adddoc__doc {
  display: flex;
  gap: 6px;
  align-items: center;
  min-width: 0;
  padding: 5px 8px;
  text-align: left;
  cursor: pointer;
  user-select: none;
  background: hsl(var(--muted) / 0.2);
  border: 1px solid hsl(var(--border) / 0.7);
  border-radius: 10px;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}

.adddoc__doc:hover {
  background: hsl(var(--primary) / 0.06);
  border-color: hsl(var(--primary) / 0.35);
}

/* 选中态：1px 主色边框 + 微光 */
.adddoc__doc--active {
  background: hsl(var(--primary) / 0.1);
  border-color: hsl(var(--primary) / 0.45);
  box-shadow: 0 0 6px hsl(var(--primary) / 18%);
}

.adddoc__doc-avatar {
  flex-shrink: 0;
  font-size: 12px;
  background: hsl(var(--primary) / 0.2);
}

.adddoc__doc-name {
  flex: 1;
  overflow: hidden;
  font-size: 13px;
  color: hsl(var(--foreground));
  text-overflow: ellipsis;
  white-space: nowrap;
}

.adddoc__doc--active .adddoc__doc-name {
  font-weight: 600;
  color: hsl(var(--primary));
}

.adddoc__doc-check {
  flex-shrink: 0;
  font-size: 14px;
  color: hsl(var(--primary));
}

/* 「专」标：医生有专属模板的小徽标 */
.adddoc__doc-personal {
  flex-shrink: 0;
  padding: 0 4px;
  font-size: 10px;
  line-height: 16px;
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 0.12);
  border-radius: 4px;
}

/* 优先专属开关行 */
.adddoc__personal-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  align-items: center;
}

.adddoc__personal-label {
  font-size: 12px;
  color: hsl(var(--foreground));
}

.adddoc__personal-hint {
  font-size: 12px;
  color: hsl(var(--muted-foreground));
}

/* 模板单选卡片 */
.adddoc__templates {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 190px;
  padding: 2px;
  overflow-y: auto;
}

.adddoc__tpl {
  display: flex;
  gap: 8px;
  align-items: center;
  padding: 8px 10px;
  text-align: left;
  cursor: pointer;
  background: hsl(var(--muted) / 0.15);
  border: 1px solid hsl(var(--border) / 0.7);
  border-radius: 10px;
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}

.adddoc__tpl:hover {
  border-color: hsl(var(--primary) / 0.35);
}

.adddoc__tpl--active {
  background: hsl(var(--primary) / 0.08);
  border-color: hsl(var(--primary) / 0.45);
  box-shadow: 0 0 6px hsl(var(--primary) / 18%);
}

.adddoc__tpl-name {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: hsl(var(--foreground));
}

.adddoc__tpl--active .adddoc__tpl-name {
  color: hsl(var(--primary));
}

.adddoc__tpl-summary {
  flex: 1;
  overflow: hidden;
  font-size: 12px;
  color: hsl(var(--muted-foreground));
  text-overflow: ellipsis;
  white-space: nowrap;
}

.adddoc__tpl-check {
  flex-shrink: 0;
  font-size: 15px;
  color: hsl(var(--primary));
}

.adddoc__custom {
  padding-top: 2px;
}
</style>
