<template>
  <div v-loading="loading" class="work-calendar">
    <!-- 工具栏 -->
    <div class="work-calendar__toolbar">
      <div class="work-calendar__toolbar-left">
        <div class="work-calendar__view-switch">
          <button
            type="button"
            class="work-calendar__view-btn"
            :class="{ 'is-active': viewMode === 'month' }"
            @click="viewMode = 'month'"
          >
            月
          </button>
          <button
            type="button"
            class="work-calendar__view-btn"
            :class="{ 'is-active': viewMode === 'week' }"
            @click="viewMode = 'week'"
          >
            周
          </button>
        </div>
        <el-button size="small" plain @click="goToday">今天</el-button>
        <el-button size="small" type="primary" plain :icon="Plus" @click="openAddForSelected">添加日程</el-button>
        <span class="work-calendar__drag-tip">按住拖动可选择日期区间</span>
      </div>
      <div class="work-calendar__toolbar-right">
        <el-popover placement="bottom-end" :width="220" trigger="click">
          <template #reference>
            <el-button size="small" text type="primary">图例</el-button>
          </template>
          <ul class="work-calendar__legend">
            <li v-for="item in legendItems" :key="item.key">
              <span class="work-calendar__legend-dot" :style="{ background: item.color }" />
              <span>{{ item.label }}</span>
            </li>
          </ul>
        </el-popover>
        <el-button size="small" text :icon="Refresh" @click="loadCalendarData" />
      </div>
    </div>

    <!-- 需关注（近期待办） -->
    <div v-if="attentionTasks.length" class="work-calendar__attention">
      <div class="work-calendar__attention-title">
        <el-icon><WarningFilled /></el-icon>
        需关注
      </div>
      <ul class="work-calendar__attention-list">
        <li
          v-for="task in attentionTasks.slice(0, 3)"
          :key="task.id"
          class="work-calendar__attention-item"
          @click="emitTaskClick(task)"
        >
          <el-tag size="small" type="danger" effect="plain">待办</el-tag>
          <span class="work-calendar__attention-text">{{ task.businessTitle }}</span>
        </li>
      </ul>
    </div>

    <!-- 月视图 -->
    <el-calendar
      v-if="viewMode === 'month'"
      v-model="calendarDate"
      class="work-calendar__panel"
      :class="{ 'is-dragging': rangeDrag.active }"
      @mouseup="finishRangeDrag"
    >
      <template #date-cell="{ data }">
        <el-tooltip
          placement="top"
          :show-after="700"
          :hide-after="80"
          effect="light"
          popper-class="work-calendar-day-tip"
          :offset="10"
          :disabled="rangeDrag.active"
        >
          <template #content>
            <div class="wc-day-tip">
              <div class="wc-day-tip__head">
                <div class="wc-day-tip__head-main">
                  <span class="wc-day-tip__date">{{ formatDayTipHead(data.day).date }}</span>
                  <span class="wc-day-tip__week">{{ formatDayTipHead(data.day).weekday }}</span>
                  <span class="wc-day-tip__lunar">{{ getDayLunarInfo(data.day).fullLunar }}</span>
                </div>
                <span v-if="getDayLunarInfo(data.day).festival" class="wc-day-tip__festival">{{ getDayLunarInfo(data.day).festival }}</span>
                <span v-else-if="formatDayTipHead(data.day).today" class="wc-day-tip__badge">今天</span>
              </div>
              <ul v-if="getHoverDayWorks(data.day).length" class="wc-day-tip__list">
                <li
                  v-for="item in getHoverDayWorks(data.day)"
                  :key="item.id"
                  class="wc-day-tip__item"
                  :class="{ 'is-clickable': item.source === 'task-wait' }"
                  @click="item.source === 'task-wait' && onEventClick(item, data.day)"
                >
                  <span class="wc-day-tip__dot" :style="{ background: eventColor(item) }" />
                  <span class="wc-day-tip__type">{{ eventTypeLabel(item) }}</span>
                  <span class="wc-day-tip__text">{{ item.content }}</span>
                </li>
              </ul>
              <div v-else class="wc-day-tip__empty">暂无安排</div>
              <div class="wc-day-tip__foot">拖动选择区间 · 双击编辑</div>
            </div>
          </template>
          <div
            class="work-calendar__cell"
            :class="[
              getCellClass(data.day, data.isSelected),
              getRangeCellClass(data.day),
              { 'is-other-month': data.type !== 'current-month' }
            ]"
            @mousedown.prevent="onRangeMouseDown(data.day)"
            @mouseenter="onRangeMouseEnter(data.day)"
            @dblclick.stop="openDayDialog(data.day)"
          >
            <div class="work-calendar__cell-head">
              <div class="work-calendar__cell-date">
                <span class="work-calendar__day">{{ dayjs(data.day).date() }}</span>
                <span
                  class="work-calendar__lunar"
                  :class="{ 'is-festival': getDayLunarInfo(data.day).isHighlight }"
                >
                  {{ getDayLunarInfo(data.day).label }}
                </span>
              </div>
            </div>
            <div v-if="getDayEvents(data.day).length" class="work-calendar__overview">
              <div
                v-for="item in getVisibleEvents(data.day).items"
                :key="item.id"
                class="work-calendar__overview-item"
                :class="overviewItemClass(item)"
                :style="{ '--item-color': eventColor(item) }"
                :title="formatEventTitle(item)"
              >
                <span class="work-calendar__overview-dot" />
                <span class="work-calendar__overview-text">{{ item.content }}</span>
              </div>
              <div v-if="getVisibleEvents(data.day).moreCount > 0" class="work-calendar__overview-more">
                +{{ getVisibleEvents(data.day).moreCount }}
              </div>
            </div>
          </div>
        </el-tooltip>
      </template>
    </el-calendar>

    <!-- 周视图 -->
    <div v-else class="work-calendar__week" :class="{ 'is-dragging': rangeDrag.active }" @mouseup="finishRangeDrag">
      <div class="work-calendar__week-nav">
        <el-button size="small" text :icon="ArrowLeft" @click="shiftWeek(-1)" />
        <span>{{ weekRangeLabel }}</span>
        <el-button size="small" text :icon="ArrowRight" @click="shiftWeek(1)" />
      </div>
      <div class="work-calendar__week-list">
        <el-tooltip
          v-for="day in weekDaysList"
          :key="day"
          placement="left"
          :show-after="2000"
          :hide-after="80"
          effect="light"
          popper-class="work-calendar-day-tip"
          :disabled="rangeDrag.active"
        >
          <template #content>
            <div class="wc-day-tip">
              <div class="wc-day-tip__head">
                <div class="wc-day-tip__head-main">
                  <span class="wc-day-tip__date">{{ formatDayTipHead(day).date }}</span>
                  <span class="wc-day-tip__week">{{ formatDayTipHead(day).weekday }}</span>
                  <span class="wc-day-tip__lunar">{{ getDayLunarInfo(day).fullLunar }}</span>
                </div>
                <span v-if="getDayLunarInfo(day).festival" class="wc-day-tip__festival">{{ getDayLunarInfo(day).festival }}</span>
                <span v-else-if="formatDayTipHead(day).today" class="wc-day-tip__badge">今天</span>
              </div>
              <ul v-if="getHoverDayWorks(day).length" class="wc-day-tip__list">
                <li
                  v-for="item in getHoverDayWorks(day)"
                  :key="item.id"
                  class="wc-day-tip__item"
                  :class="{ 'is-clickable': item.source === 'task-wait' }"
                  @click="item.source === 'task-wait' && onEventClick(item, day)"
                >
                  <span class="wc-day-tip__dot" :style="{ background: eventColor(item) }" />
                  <span class="wc-day-tip__type">{{ eventTypeLabel(item) }}</span>
                  <span class="wc-day-tip__text">{{ item.content }}</span>
                </li>
              </ul>
              <div v-else class="wc-day-tip__empty">暂无安排</div>
            </div>
          </template>
          <div
            class="work-calendar__week-row"
            :class="[
              { 'is-selected': day === selectedDay, 'is-today': isToday(day), 'is-weekend': isWeekend(day) },
              getRangeCellClass(day)
            ]"
            @mousedown.prevent="onRangeMouseDown(day)"
            @mouseenter="onRangeMouseEnter(day)"
            @dblclick.stop="openDayDialog(day)"
          >
            <div class="work-calendar__week-date">
              <span class="work-calendar__week-weekday">{{ weekDayShort(day) }}</span>
              <span class="work-calendar__week-num" :class="{ 'is-today-num': isToday(day) }">{{ dayjs(day).date() }}</span>
              <span
                class="work-calendar__week-lunar"
                :class="{ 'is-festival': getDayLunarInfo(day).isHighlight }"
              >
                {{ getDayLunarInfo(day).label }}
              </span>
            </div>
            <div class="work-calendar__week-events">
              <div
                v-for="item in getDayEvents(day).slice(0, 3)"
                :key="item.id"
                class="work-calendar__overview-item work-calendar__overview-item--row"
                :class="overviewItemClass(item)"
                :style="{ '--item-color': eventColor(item) }"
                :title="formatEventTitle(item)"
                @click.stop="onEventClick(item, day)"
              >
                <span class="work-calendar__overview-dot" />
                <span class="work-calendar__overview-tag">{{ eventTypeLabel(item) }}</span>
                <span class="work-calendar__overview-text">{{ item.content }}</span>
              </div>
              <span v-if="!getDayEvents(day).length" class="work-calendar__week-empty">—</span>
            </div>
          </div>
        </el-tooltip>
      </div>
    </div>

    <!-- 维护弹窗 -->
    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="540px" append-to-body destroy-on-close @closed="resetDialog">
      <div v-if="taskWorksForDialog.length" class="work-calendar__dialog-section">
        <div class="work-calendar__dialog-label">待办审批</div>
        <ul class="work-calendar__dialog-readonly">
          <li v-for="item in taskWorksForDialog" :key="item.id" class="work-calendar__dialog-task" @click="onEventClick(item, dialogDay)">
            <el-tag size="small" type="danger" effect="plain">待办</el-tag>
            <span>{{ item.content }}</span>
            <el-icon class="work-calendar__dialog-arrow"><ArrowRight /></el-icon>
          </li>
        </ul>
      </div>

      <div class="work-calendar__dialog-section">
        <div class="work-calendar__dialog-label">新建日程</div>
        <el-form label-width="72px" size="default">
          <el-form-item label="日期">
            <el-date-picker
              v-model="dialogDateRange"
              type="daterange"
              range-separator="至"
              start-placeholder="开始"
              end-placeholder="结束"
              value-format="YYYY-MM-DD"
              style="width: 100%"
            />
          </el-form-item>
          <el-form-item label="类型">
            <el-radio-group v-model="editForm.type">
              <el-radio-button v-for="t in scheduleTypes" :key="t.value" :value="t.value">{{ t.label }}</el-radio-button>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="优先级">
            <el-select v-model="editForm.priority" style="width: 120px">
              <el-option label="普通" value="normal" />
              <el-option label="重要" value="high" />
              <el-option label="较低" value="low" />
            </el-select>
          </el-form-item>
          <el-form-item label="内容">
            <el-input
              v-model="editForm.content"
              type="textarea"
              :rows="3"
              maxlength="500"
              show-word-limit
              placeholder="请输入日程安排"
            />
          </el-form-item>
        </el-form>
        <div class="work-calendar__dialog-actions">
          <el-button type="primary" :disabled="!editForm.content?.trim()" @click="saveManualWork">
            {{ editingId ? '保存修改' : '添加日程' }}
          </el-button>
          <el-button v-if="editingId" @click="cancelEdit">取消编辑</el-button>
        </div>
      </div>

      <div v-if="manualWorksForDialog.length" class="work-calendar__dialog-section">
        <div class="work-calendar__dialog-label">已维护记录</div>
        <ul class="work-calendar__dialog-manual">
          <li v-for="item in manualWorksForDialog" :key="item.id">
            <div class="work-calendar__manual-main">
              <el-tag size="small" :type="typeTagType(item.type)" effect="plain">{{ typeLabel(item.type) }}</el-tag>
              <span v-if="item.rangeStart && item.rangeEnd && item.rangeStart !== item.rangeEnd" class="work-calendar__manual-range">
                {{ formatRangeLabel(item.rangeStart, item.rangeEnd) }}
              </span>
              <span>{{ item.content }}</span>
            </div>
            <div class="work-calendar__manual-ops">
              <el-button link type="primary" @click="startEdit(item)">编辑</el-button>
              <el-button link type="danger" @click="removeManualWork(item)">删除</el-button>
            </div>
          </li>
        </ul>
      </div>

      <template #footer>
        <el-button @click="dialogVisible = false">关闭</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup lang="ts">
import dayjs from 'dayjs';
import { ArrowLeft, ArrowRight, Plus, Refresh, WarningFilled } from '@element-plus/icons-vue';
import { useUserStore } from '@/store/modules/user';
import { getDayLunarInfo } from '@/utils/lunarCalendar';
import type { CalendarPendingTask } from './workCalendar.types';

type WorkSource = 'manual' | 'task-wait';
type ScheduleType = 'work' | 'meeting' | 'trip' | 'activity';
type Priority = 'low' | 'normal' | 'high';

interface WorkCalendarItem {
  id: string;
  content: string;
  source: WorkSource;
  type?: ScheduleType;
  priority?: Priority;
  taskRef?: CalendarPendingTask;
  rangeId?: string;
  rangeStart?: string;
  rangeEnd?: string;
}

const props = defineProps<{
  active?: boolean;
  pendingTasks?: CalendarPendingTask[];
}>();

const emit = defineEmits<{
  taskClick: [task: CalendarPendingTask];
}>();

const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStore();

const loading = ref(false);
const viewMode = ref<'month' | 'week'>('month');
const calendarDate = ref(new Date());
const weekAnchor = ref(dayjs().format('YYYY-MM-DD'));
const selectedDay = ref('');
const dialogVisible = ref(false);
const dialogDay = ref('');
const dialogRangeStart = ref('');
const dialogRangeEnd = ref('');
const editingId = ref('');
const editingRangeId = ref('');

const rangeDrag = reactive({
  active: false,
  anchor: '',
  current: '',
  moved: false
});

const editForm = reactive({
  content: '',
  type: 'work' as ScheduleType,
  priority: 'normal' as Priority
});

const manualWorkMap = ref<Record<string, WorkCalendarItem[]>>({});
const taskWorkMap = ref<Record<string, WorkCalendarItem[]>>({});

const scheduleTypes = [
  { value: 'work' as ScheduleType, label: '工作' },
  { value: 'meeting' as ScheduleType, label: '会议' },
  { value: 'trip' as ScheduleType, label: '出差' },
  { value: 'activity' as ScheduleType, label: '活动' }
];

const legendItems = [
  { key: 'work', label: '工作', color: '#409eff' },
  { key: 'meeting', label: '会议', color: '#e6a23c' },
  { key: 'trip', label: '出差', color: '#9b59b6' },
  { key: 'activity', label: '活动', color: '#00bcd4' },
  { key: 'task', label: '待办审批', color: '#f56c6c' }
];

const typeColors: Record<ScheduleType, string> = {
  work: '#409eff',
  meeting: '#e6a23c',
  trip: '#9b59b6',
  activity: '#00bcd4'
};

const weekDayNames = ['日', '一', '二', '三', '四', '五', '六'];
const weekDayFull = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];

const storageKey = computed(() => `hwbm_work_calendar_v2_${userStore.userId || 'guest'}`);

const weekRangeLabel = computed(() => {
  const monday = dayjs(getWeekMonday(weekAnchor.value));
  const sunday = monday.add(6, 'day');
  return `${monday.format('M月D日')} - ${sunday.format('M月D日')}`;
});

const weekDaysList = computed(() => {
  const monday = getWeekMonday(weekAnchor.value);
  return Array.from({ length: 7 }, (_, i) => dayjs(monday).add(i, 'day').format('YYYY-MM-DD'));
});

const attentionTasks = computed(() => {
  const tasks = props.pendingTasks || [];
  const today = dayjs();
  return tasks.filter((t) => {
    if (!t.createTime) return true;
    const d = dayjs(t.createTime);
    return d.isAfter(today.subtract(4, 'day'));
  });
});

const dialogTitle = computed(() => {
  if (!dialogDay.value) return '日程维护';
  const start = dialogRangeStart.value || dialogDay.value;
  const end = dialogRangeEnd.value || dialogDay.value;
  if (start !== end) {
    return `${formatRangeLabel(start, end)} · 日程维护`;
  }
  return `${formatDayLabel(dialogDay.value)} · 日程维护`;
});

const dialogDateRange = computed({
  get: () => {
    const start = dialogRangeStart.value || dialogDay.value;
    const end = dialogRangeEnd.value || dialogDay.value;
    return start && end ? [start, end] : [];
  },
  set: (val: string[] | null) => {
    if (val?.length === 2) {
      dialogRangeStart.value = val[0];
      dialogRangeEnd.value = val[1];
      dialogDay.value = val[0];
    }
  }
});

const manualWorksForDialog = computed(() => {
  if (!dialogDay.value) return [];
  const day = dialogDay.value;
  const seen = new Set<string>();
  return (manualWorkMap.value[day] || []).filter((item) => {
    if (!item.rangeId) return true;
    if (seen.has(item.rangeId)) return false;
    seen.add(item.rangeId);
    return true;
  });
});

const taskWorksForDialog = computed(() => (dialogDay.value ? taskWorkMap.value[dialogDay.value] || [] : []));

const getWeekMonday = (day: string) => {
  const d = dayjs(day);
  const dayNum = d.day();
  const mondayOffset = dayNum === 0 ? -6 : 1 - dayNum;
  return d.add(mondayOffset, 'day').format('YYYY-MM-DD');
};

const getHoverDayWorks = (day: string): WorkCalendarItem[] => {
  const tasks = taskWorkMap.value[day] || [];
  const manual = manualWorkMap.value[day] || [];

  return [...tasks, ...manual]
    .filter((item) => item.content?.trim())
    .sort((a, b) => {
      const score = (e: WorkCalendarItem) => (e.source === 'task-wait' ? 0 : e.priority === 'high' ? 1 : 2);
      return score(a) - score(b);
    });
};

const formatDayLabel = (day: string) => {
  const d = dayjs(day);
  return `${d.format('YYYY年MM月DD日')} ${weekDayFull[d.day()]}`;
};

const formatRangeLabel = (start: string, end: string) => {
  const s = dayjs(start);
  const e = dayjs(end);
  if (s.isSame(e, 'day')) return formatDayLabel(start);
  if (s.isSame(e, 'year')) return `${s.format('M月D日')} - ${e.format('M月D日')}`;
  return `${s.format('YYYY年M月D日')} - ${e.format('YYYY年M月D日')}`;
};

const normalizeRange = (a: string, b: string) => {
  if (!a || !b) return { start: a || b, end: a || b };
  return dayjs(a).isAfter(dayjs(b), 'day') ? { start: b, end: a } : { start: a, end: b };
};

const enumerateRangeDays = (start: string, end: string) => {
  const { start: rs, end: re } = normalizeRange(start, end);
  const days: string[] = [];
  let cursor = dayjs(rs);
  const last = dayjs(re);
  while (cursor.isBefore(last, 'day') || cursor.isSame(last, 'day')) {
    days.push(cursor.format('YYYY-MM-DD'));
    cursor = cursor.add(1, 'day');
  }
  return days;
};

const isDayInRange = (day: string, start: string, end: string) => {
  if (!start || !end) return false;
  const { start: rs, end: re } = normalizeRange(start, end);
  const d = dayjs(day);
  return (d.isAfter(dayjs(rs), 'day') || d.isSame(dayjs(rs), 'day')) && (d.isBefore(dayjs(re), 'day') || d.isSame(dayjs(re), 'day'));
};

const getDragPreviewRange = () => {
  if (!rangeDrag.active || !rangeDrag.anchor) return null;
  return normalizeRange(rangeDrag.anchor, rangeDrag.current || rangeDrag.anchor);
};

const getRangeCellClass = (day: string) => {
  const preview = getDragPreviewRange();
  if (preview && isDayInRange(day, preview.start, preview.end)) {
    const isStart = day === preview.start;
    const isEnd = day === preview.end;
    return {
      'is-range-preview': true,
      'is-range-start': isStart,
      'is-range-end': isEnd,
      'is-range-middle': !isStart && !isEnd
    };
  }
  return {};
};

const formatDayTipHead = (day: string) => {
  const d = dayjs(day);
  return {
    date: d.format('M月D日'),
    weekday: weekDayFull[d.day()],
    today: isToday(day)
  };
};

const weekDayShort = (day: string) => `周${weekDayNames[dayjs(day).day()]}`;

const isToday = (day: string) => dayjs(day).isSame(dayjs(), 'day');

const isWeekend = (day: string) => {
  const d = dayjs(day).day();
  return d === 0 || d === 6;
};

const typeLabel = (type?: ScheduleType) => scheduleTypes.find((t) => t.value === type)?.label || '工作';

const typeTagType = (type?: ScheduleType): 'primary' | 'warning' | 'success' | 'info' => {
  if (type === 'meeting') return 'warning';
  if (type === 'trip') return 'info';
  if (type === 'activity') return 'success';
  return 'primary';
};

const eventTypeLabel = (item: WorkCalendarItem) => {
  if (item.source === 'task-wait') return '待办';
  return typeLabel(item.type);
};

const eventTagType = (item: WorkCalendarItem): 'primary' | 'warning' | 'success' | 'info' | 'danger' => {
  if (item.source === 'task-wait') return 'danger';
  return typeTagType(item.type);
};

const eventColor = (item: WorkCalendarItem) => {
  if (item.source === 'task-wait') return '#f56c6c';
  if (item.priority === 'high') return '#f56c6c';
  return typeColors[item.type || 'work'];
};

const formatEventTitle = (item: WorkCalendarItem) => {
  if (item.rangeStart && item.rangeEnd && item.rangeStart !== item.rangeEnd) {
    return `[${formatRangeLabel(item.rangeStart, item.rangeEnd)}] ${item.content}`;
  }
  return item.content;
};

const overviewItemClass = (item: WorkCalendarItem) => {
  if (item.source === 'task-wait') return 'is-task';
  if (item.priority === 'high') return 'is-urgent';
  return `is-${item.type || 'work'}`;
};

const getCellClass = (day: string, isSelected: boolean) => ({
  'is-selected': day === selectedDay.value || isSelected,
  'is-today': isToday(day),
  'is-weekend': isWeekend(day),
  'has-work': getDayEvents(day).length > 0,
  'has-urgent': getDayEvents(day).some((e) => e.priority === 'high' || e.source === 'task-wait')
});

const getDayEvents = (day: string): WorkCalendarItem[] => {
  const manual = manualWorkMap.value[day] || [];
  const tasks = taskWorkMap.value[day] || [];
  return [...tasks, ...manual].sort((a, b) => {
    const score = (e: WorkCalendarItem) => (e.source === 'task-wait' ? 0 : e.priority === 'high' ? 1 : 2);
    return score(a) - score(b);
  });
};

const getVisibleEvents = (day: string) => {
  const events = getDayEvents(day);
  return { items: events.slice(0, 2), moreCount: Math.max(0, events.length - 2) };
};

const loadManualFromStorage = () => {
  try {
    const raw = localStorage.getItem(storageKey.value);
    if (raw) {
      manualWorkMap.value = JSON.parse(raw);
      return;
    }
    const legacyKey = `hwbm_work_calendar_${userStore.userId || 'guest'}`;
    const legacy = localStorage.getItem(legacyKey);
    if (legacy) {
      const parsed = JSON.parse(legacy) as Record<string, WorkCalendarItem[]>;
      manualWorkMap.value = Object.fromEntries(
        Object.entries(parsed).map(([day, items]) => [
          day,
          items.map((item) => ({ ...item, type: item.type || 'work', priority: item.priority || 'normal' }))
        ])
      );
    }
  } catch {
    manualWorkMap.value = {};
  }
};

const persistManualToStorage = () => {
  localStorage.setItem(storageKey.value, JSON.stringify(manualWorkMap.value));
};

const buildTaskWorkMap = () => {
  const map: Record<string, WorkCalendarItem[]> = {};
  (props.pendingTasks || []).forEach((task) => {
    if (!task.createTime) return;
    const day = dayjs(task.createTime).format('YYYY-MM-DD');
    if (!map[day]) map[day] = [];
    map[day].push({
      id: `task-${task.id}`,
      content: task.businessTitle || '待办审批',
      source: 'task-wait',
      priority: 'high',
      taskRef: task
    });
  });
  taskWorkMap.value = map;
};

const loadCalendarData = async () => {
  loading.value = true;
  try {
    loadManualFromStorage();
    buildTaskWorkMap();
    if (!selectedDay.value) selectedDay.value = dayjs().format('YYYY-MM-DD');
  } finally {
    loading.value = false;
  }
};

const selectDay = (day: string) => {
  selectedDay.value = day;
  if (viewMode.value === 'week') weekAnchor.value = day;
};

const goToday = () => {
  const today = dayjs().format('YYYY-MM-DD');
  calendarDate.value = new Date();
  weekAnchor.value = today;
  selectedDay.value = today;
};

const shiftWeek = (offset: number) => {
  weekAnchor.value = dayjs(getWeekMonday(weekAnchor.value)).add(offset * 7, 'day').format('YYYY-MM-DD');
  selectedDay.value = weekAnchor.value;
};

const openDayDialog = (day: string, range?: { start: string; end: string }) => {
  selectedDay.value = day;
  dialogDay.value = day;
  if (range) {
    dialogRangeStart.value = range.start;
    dialogRangeEnd.value = range.end;
  } else {
    dialogRangeStart.value = day;
    dialogRangeEnd.value = day;
  }
  resetDialog();
  dialogVisible.value = true;
};

const onRangeMouseDown = (day: string) => {
  rangeDrag.active = true;
  rangeDrag.anchor = day;
  rangeDrag.current = day;
  rangeDrag.moved = false;
};

const onRangeMouseEnter = (day: string) => {
  if (!rangeDrag.active) return;
  if (day !== rangeDrag.current) rangeDrag.moved = true;
  rangeDrag.current = day;
};

const finishRangeDrag = () => {
  if (!rangeDrag.active) return;

  const anchor = rangeDrag.anchor;
  const current = rangeDrag.current || anchor;
  const { start, end } = normalizeRange(anchor, current);
  const moved = rangeDrag.moved;

  rangeDrag.active = false;
  rangeDrag.anchor = '';
  rangeDrag.current = '';
  rangeDrag.moved = false;

  selectedDay.value = end;
  if (viewMode.value === 'week') weekAnchor.value = end;

  if (moved || start !== end) {
    openDayDialog(start, { start, end });
  } else {
    selectDay(anchor);
  }
};

const openAddForSelected = () => {
  const day = selectedDay.value || dayjs().format('YYYY-MM-DD');
  openDayDialog(day);
};

const resetDialog = () => {
  editingId.value = '';
  editingRangeId.value = '';
  editForm.content = '';
  editForm.type = 'work';
  editForm.priority = 'normal';
};

const startEdit = (item: WorkCalendarItem) => {
  editingId.value = item.id;
  editingRangeId.value = item.rangeId || '';
  editForm.content = item.content;
  editForm.type = item.type || 'work';
  editForm.priority = item.priority || 'normal';
  if (item.rangeStart && item.rangeEnd) {
    dialogRangeStart.value = item.rangeStart;
    dialogRangeEnd.value = item.rangeEnd;
  }
};

const removeRangeItems = (rangeId: string) => {
  const next = { ...manualWorkMap.value };
  Object.keys(next).forEach((day) => {
    next[day] = (next[day] || []).filter((w) => w.rangeId !== rangeId);
    if (!next[day].length) delete next[day];
  });
  manualWorkMap.value = next;
};

const findManualItem = (id: string): WorkCalendarItem | undefined => {
  for (const list of Object.values(manualWorkMap.value)) {
    const found = list.find((w) => w.id === id);
    if (found) return found;
  }
  return undefined;
};

const saveManualWork = () => {
  const content = editForm.content?.trim();
  const start = dialogRangeStart.value || dialogDay.value;
  const end = dialogRangeEnd.value || dialogDay.value;
  if (!content || !start || !end) return;

  const { start: rs, end: re } = normalizeRange(start, end);
  const days = enumerateRangeDays(rs, re);
  const isMultiDay = days.length > 1;

  if (editingId.value) {
    const existing = findManualItem(editingId.value);
    if (existing?.rangeId) {
      removeRangeItems(existing.rangeId);
    } else {
      const next = { ...manualWorkMap.value };
      Object.keys(next).forEach((day) => {
        const list = (next[day] || []).filter((w) => w.id !== editingId.value);
        if (list.length) next[day] = list;
        else delete next[day];
      });
      manualWorkMap.value = next;
    }
  }

  const rangeId = isMultiDay ? editingRangeId.value || `range-${Date.now()}` : undefined;
  const newMap = { ...manualWorkMap.value };
  const baseId = Date.now();

  days.forEach((day, index) => {
    const list = [...(newMap[day] || [])];
    list.push({
      id: isMultiDay ? `${rangeId}-${index}` : `manual-${baseId}`,
      content,
      source: 'manual',
      type: editForm.type,
      priority: editForm.priority,
      rangeId,
      rangeStart: isMultiDay ? rs : undefined,
      rangeEnd: isMultiDay ? re : undefined
    });
    newMap[day] = list;
  });

  manualWorkMap.value = newMap;
  persistManualToStorage();
  proxy?.$modal.msgSuccess(editingId.value ? '已更新' : isMultiDay ? `已添加 ${days.length} 天日程` : '已添加');
  resetDialog();
};

const removeManualWork = (item: WorkCalendarItem) => {
  if (item.rangeId) {
    removeRangeItems(item.rangeId);
  } else if (dialogDay.value) {
    const day = dialogDay.value;
    const list = (manualWorkMap.value[day] || []).filter((w) => w.id !== item.id);
    const next = { ...manualWorkMap.value };
    if (list.length) next[day] = list;
    else delete next[day];
    manualWorkMap.value = next;
  }
  persistManualToStorage();
  if (editingId.value === item.id) resetDialog();
};

const cancelEdit = () => resetDialog();

const onDocumentMouseUp = () => {
  if (rangeDrag.active) finishRangeDrag();
};

onMounted(() => {
  document.addEventListener('mouseup', onDocumentMouseUp);
});

onBeforeUnmount(() => {
  document.removeEventListener('mouseup', onDocumentMouseUp);
});

const emitTaskClick = (task: CalendarPendingTask) => emit('taskClick', task);

const onEventClick = (item: WorkCalendarItem, _day: string) => {
  if (item.source === 'task-wait' && item.taskRef) {
    emitTaskClick(item.taskRef);
  }
};

watch(
  () => props.pendingTasks,
  () => {
    if (props.active) buildTaskWorkMap();
  },
  { deep: true }
);

watch(
  () => props.active,
  (val) => {
    if (val) loadCalendarData();
  },
  { immediate: true }
);

defineExpose({ reload: loadCalendarData });
</script>

<style lang="scss" scoped>
.work-calendar {
  --wc-primary: #1e90ff;
  --wc-primary-soft: rgba(30, 144, 255, 0.08);
  --wc-radius: 8px;

  &__toolbar {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    margin-bottom: 10px;
    gap: 8px;
  }

  &__toolbar-left,
  &__toolbar-right {
    display: flex;
    align-items: center;
    gap: 6px;
  }

  &__toolbar-left {
    flex: 1;
    min-width: 0;
    flex-wrap: wrap;
  }

  &__toolbar-right {
    flex-shrink: 0;
  }

  &__view-switch {
    display: inline-flex;
    flex-shrink: 0;
    border: 1px solid var(--el-border-color);
    border-radius: 4px;
    overflow: hidden;
    background: var(--el-fill-color-blank);
  }

  &__view-btn {
    border: none;
    background: transparent;
    padding: 6px 14px;
    min-width: 36px;
    font-size: 12px;
    line-height: 1;
    cursor: pointer;
    color: var(--el-text-color-regular);
    transition: background 0.15s, color 0.15s;

    & + & {
      border-left: 1px solid var(--el-border-color);
    }

    &.is-active {
      background: var(--el-color-primary);
      color: #fff;
    }

    &:hover:not(.is-active) {
      background: var(--el-fill-color-light);
    }
  }

  &__toolbar-left > .el-button {
    flex-shrink: 0;
  }

  &__drag-tip {
    flex: 1 1 100%;
    font-size: 11px;
    color: var(--el-text-color-secondary);
    line-height: 1.4;
  }

  &__panel.is-dragging,
  &__week.is-dragging {
    user-select: none;
    cursor: crosshair;
  }

  &__attention {
    margin-bottom: 10px;
    padding: 10px 12px;
    background: linear-gradient(135deg, #fff5f5 0%, #fff 100%);
    border: 1px solid var(--el-color-danger-light-7);
    border-left: 3px solid var(--el-color-danger);
    border-radius: var(--wc-radius);
  }

  &__attention-title {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: 12px;
    font-weight: 600;
    color: var(--el-color-danger);
    margin-bottom: 6px;
  }

  &__attention-list {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  &__attention-item {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 5px 6px;
    margin: 0 -6px;
    font-size: 12px;
    border-radius: 4px;
    cursor: pointer;
    transition: background 0.15s;

    &:hover {
      background: rgba(245, 108, 108, 0.08);

      .work-calendar__attention-text {
        color: var(--el-color-primary);
      }
    }
  }

  &__attention-text {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--el-text-color-regular);
  }

  &__legend {
    list-style: none;
    margin: 0;
    padding: 0;

    li {
      display: flex;
      align-items: center;
      gap: 8px;
      padding: 5px 0;
      font-size: 12px;
      color: var(--el-text-color-regular);
    }
  }

  &__legend-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
  }

  &__panel {
    padding: 8px;
    background: var(--el-bg-color);
    border: 1px solid var(--el-border-color-lighter);
    border-radius: var(--wc-radius);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);

    :deep(.el-calendar__header) {
      padding: 0 4px 10px;
      border-bottom: 1px solid var(--el-border-color-extra-light);
    }

    :deep(.el-calendar__title) {
      font-size: 14px;
      font-weight: 600;
      color: var(--el-text-color-primary);
    }

    :deep(.el-calendar__button-group) {
      .el-button {
        padding: 4px 8px;
        font-size: 12px;
      }
    }

    :deep(.el-calendar__body) {
      padding: 8px 0 0;
    }

    :deep(.el-calendar-table thead th) {
      padding: 6px 0;
      font-size: 11px;
      font-weight: 500;
      color: var(--el-text-color-secondary);
      border: none;
    }

    :deep(.el-calendar-table .el-calendar-day) {
      height: 66px;
      padding: 1px;
    }

    :deep(.el-calendar-table td) {
      border: none;
    }

    :deep(.el-calendar-table tr + tr td) {
      padding-top: 2px;
    }

    :deep(.prev .el-calendar-day),
    :deep(.next .el-calendar-day) {
      opacity: 0.45;
    }

    :deep(.el-calendar-day .el-tooltip__trigger) {
      display: block;
      width: 100%;
      height: 100%;
    }
  }

  &__cell {
    width: 100%;
    height: 100%;
    min-height: 64px;
    display: flex;
    flex-direction: column;
    padding: 2px 3px 3px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s, box-shadow 0.15s;

    &:hover {
      background: var(--el-fill-color-light);
    }

    &.is-other-month {
      .work-calendar__day,
      .work-calendar__lunar {
        color: var(--el-text-color-placeholder);
      }

      .work-calendar__lunar.is-festival {
        color: var(--el-color-danger-light-3);
      }
    }

    &.is-selected {
      background: var(--wc-primary-soft);
      box-shadow: inset 0 0 0 1.5px rgba(30, 144, 255, 0.35);
    }

    &.is-today:not(.is-selected) {
      box-shadow: inset 0 0 0 1.5px rgba(30, 144, 255, 0.25);
    }

    &.is-today .work-calendar__day {
      color: #fff;
      background: linear-gradient(135deg, #0047ab, var(--wc-primary));
      border-radius: 50%;
      width: 20px;
      height: 20px;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      font-weight: 600;
      box-shadow: 0 2px 6px rgba(30, 144, 255, 0.35);
    }

    &.is-weekend:not(.is-selected):not(.is-today) {
      background: rgba(0, 0, 0, 0.015);
    }

    &.has-work:not(.is-selected) {
      background: rgba(30, 144, 255, 0.04);
    }

    &.has-urgent:not(.is-selected) {
      box-shadow: inset 0 0 0 1.5px rgba(245, 108, 108, 0.45);
    }

    &.is-range-preview {
      background: rgba(30, 144, 255, 0.14);
      box-shadow: inset 0 0 0 1.5px rgba(30, 144, 255, 0.45);
    }

    &.is-range-preview.is-range-start {
      border-top-left-radius: 6px;
      border-bottom-left-radius: 6px;
    }

    &.is-range-preview.is-range-end {
      border-top-right-radius: 6px;
      border-bottom-right-radius: 6px;
    }
  }

  &__cell-head {
    flex-shrink: 0;
    line-height: 1;
  }

  &__cell-date {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 2px;
  }

  &__lunar {
    font-size: 10px;
    color: var(--el-text-color-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
    min-width: 0;
    text-align: right;
    line-height: 1.2;

    &.is-festival {
      color: var(--el-color-danger);
      font-weight: 500;
    }
  }

  &__day {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    font-size: 12px;
    font-weight: 500;
    color: var(--el-text-color-primary);
  }

  &__overview {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 0;
    margin-top: 2px;
    overflow: hidden;
  }

  &__overview-item {
    --item-color: var(--el-color-primary);
    display: flex;
    align-items: center;
    gap: 3px;
    min-width: 0;
    padding: 1px 4px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--item-color) 10%, var(--el-bg-color));
    box-shadow: inset 2px 0 0 var(--item-color);

    &.is-task,
    &.is-urgent {
      background: color-mix(in srgb, var(--item-color) 12%, #fff);
    }

    &--row {
      padding: 3px 6px;
      margin-bottom: 3px;
      border-radius: 5px;
      box-shadow: inset 3px 0 0 var(--item-color);
      cursor: pointer;
      transition: transform 0.12s, background 0.12s;

      .work-calendar__overview-text {
        font-size: 11px;
      }

      &:hover {
        transform: translateX(2px);
        background: color-mix(in srgb, var(--item-color) 16%, var(--el-bg-color));
      }

      &:last-child {
        margin-bottom: 0;
      }
    }
  }

  &__overview-dot {
    flex-shrink: 0;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--item-color);
  }

  &__overview-tag {
    flex-shrink: 0;
    font-size: 9px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--item-color);
    opacity: 0.95;
  }

  &__overview-text {
    flex: 1;
    min-width: 0;
    font-size: 10px;
    line-height: 1.25;
    color: var(--el-text-color-regular);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  &__overview-more {
    flex-shrink: 0;
    padding: 0 4px;
    font-size: 9px;
    font-weight: 600;
    line-height: 14px;
    text-align: center;
    color: var(--el-color-primary);
    background: var(--wc-primary-soft);
    border-radius: 3px;
  }

  &__week {
    padding: 8px;
    background: var(--el-bg-color);
    border: 1px solid var(--el-border-color-lighter);
    border-radius: var(--wc-radius);
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.03);
  }

  &__week-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 8px;
    padding-bottom: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--el-text-color-primary);
    border-bottom: 1px solid var(--el-border-color-extra-light);
  }

  &__week-list {
    max-height: 260px;
    overflow-y: auto;
    padding-right: 2px;

    :deep(.el-tooltip__trigger) {
      display: block;
      width: 100%;
    }

    &::-webkit-scrollbar {
      width: 4px;
    }

    &::-webkit-scrollbar-thumb {
      background: var(--el-border-color);
      border-radius: 2px;
    }
  }

  &__week-row {
    display: flex;
    gap: 10px;
    width: 100%;
    padding: 8px 6px;
    margin-bottom: 4px;
    cursor: pointer;
    border-radius: 6px;
    transition: background 0.15s;

    &:hover {
      background: var(--el-fill-color-light);
    }

    &.is-selected {
      background: var(--wc-primary-soft);
      box-shadow: inset 0 0 0 1px rgba(30, 144, 255, 0.2);
    }

    &.is-today.is-selected {
      background: var(--wc-primary-soft);
    }

    &.is-weekend .work-calendar__week-weekday {
      color: var(--el-text-color-placeholder);
    }

    &.is-range-preview {
      background: rgba(30, 144, 255, 0.14);
      box-shadow: inset 0 0 0 1.5px rgba(30, 144, 255, 0.45);
    }
  }

  &__week-date {
    flex-shrink: 0;
    width: 42px;
    text-align: center;
  }

  &__week-lunar {
    display: block;
    margin-top: 2px;
    font-size: 10px;
    color: var(--el-text-color-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    line-height: 1.2;

    &.is-festival {
      color: var(--el-color-danger);
      font-weight: 500;
    }
  }

  &__week-weekday {
    display: block;
    font-size: 10px;
    color: var(--el-text-color-secondary);
    line-height: 1.2;
  }

  &__week-num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 26px;
    margin-top: 2px;
    font-size: 14px;
    font-weight: 600;
    border-radius: 50%;

    &.is-today-num {
      color: #fff;
      background: linear-gradient(135deg, #0047ab, var(--wc-primary));
      box-shadow: 0 2px 6px rgba(30, 144, 255, 0.3);
    }
  }

  &__week-events {
    flex: 1;
    min-width: 0;
    padding-top: 2px;
  }

  &__week-empty {
    font-size: 12px;
    color: var(--el-text-color-placeholder);
    line-height: 26px;
  }

  &__dialog-section {
    margin-bottom: 16px;
  }

  &__dialog-label {
    margin-bottom: 8px;
    padding-left: 8px;
    font-size: 13px;
    font-weight: 600;
    color: var(--el-text-color-primary);
    border-left: 3px solid var(--wc-primary);
  }

  &__dialog-readonly,
  &__dialog-manual {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  &__dialog-readonly li,
  &__dialog-task {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 0;
    font-size: 13px;
    border-bottom: 1px dashed var(--el-border-color-lighter);

    &:last-child {
      border-bottom: none;
    }
  }

  &__dialog-task {
    padding: 8px 6px;
    margin: 0 -6px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s;

    &:hover {
      background: var(--el-fill-color-light);
      color: var(--el-color-primary);
    }
  }

  &__dialog-arrow {
    margin-left: auto;
    color: var(--el-text-color-placeholder);
  }

  &__dialog-manual li {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 8px;
    padding: 8px 0;
    border-bottom: 1px dashed var(--el-border-color-lighter);

    &:last-child {
      border-bottom: none;
    }
  }

  &__manual-main {
    flex: 1;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    font-size: 13px;
    line-height: 1.5;
  }

  &__manual-range {
    font-size: 12px;
    color: var(--el-color-primary);
    padding: 0 6px;
    background: var(--wc-primary-soft);
    border-radius: 4px;
  }

  &__manual-ops {
    flex-shrink: 0;
  }

  &__dialog-actions {
    display: flex;
    gap: 8px;
    margin-top: 4px;
  }
}
</style>

<style lang="scss">
/* 日期悬停浮层（teleport 到 body，不可 scoped） */
.work-calendar-day-tip.el-popper {
  padding: 0 !important;
  border: 1px solid rgba(30, 144, 255, 0.18) !important;
  border-radius: 10px !important;
  box-shadow:
    0 8px 24px rgba(0, 71, 171, 0.12),
    0 2px 8px rgba(0, 0, 0, 0.06) !important;
  overflow: hidden;

  .el-popper__arrow::before {
    border-color: rgba(30, 144, 255, 0.18) !important;
  }
}

.wc-day-tip {
  min-width: 200px;
  max-width: 280px;

  &__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 10px 12px;
    background: linear-gradient(135deg, #0047ab 0%, #1e90ff 100%);
    color: #fff;
  }

  &__head-main {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    min-width: 0;
  }

  &__date {
    font-size: 15px;
    font-weight: 700;
    white-space: nowrap;
    line-height: 1.2;
  }

  &__week {
    font-size: 12px;
    opacity: 0.9;
    white-space: nowrap;
  }

  &__lunar {
    font-size: 11px;
    opacity: 0.85;
    white-space: nowrap;
  }

  &__festival {
    flex-shrink: 0;
    padding: 1px 8px;
    font-size: 10px;
    font-weight: 600;
    color: #f56c6c;
    background: #fff;
    border-radius: 10px;
  }

  &__badge {
    flex-shrink: 0;
    padding: 1px 8px;
    font-size: 10px;
    font-weight: 600;
    color: #1e90ff;
    background: #fff;
    border-radius: 10px;
  }

  &__list {
    list-style: none;
    margin: 0;
    padding: 6px 0;
    max-height: 200px;
    overflow-y: auto;
  }

  &__item {
    display: grid;
    grid-template-columns: 6px auto 1fr;
    align-items: start;
    gap: 6px 8px;
    padding: 7px 12px;
    font-size: 12px;
    line-height: 1.4;
    border-bottom: 1px solid var(--el-border-color-extra-light);
    transition: background 0.12s;

    &:last-child {
      border-bottom: none;
    }

    &.is-clickable {
      cursor: pointer;

      &:hover {
        background: var(--el-fill-color-light);

        .wc-day-tip__text {
          color: var(--el-color-primary);
        }
      }
    }
  }

  &__dot {
    width: 6px;
    height: 6px;
    margin-top: 5px;
    border-radius: 50%;
    flex-shrink: 0;
  }

  &__type {
    font-size: 10px;
    font-weight: 600;
    color: var(--el-text-color-secondary);
    white-space: nowrap;
  }

  &__text {
    color: var(--el-text-color-primary);
    word-break: break-all;
  }

  &__empty {
    padding: 16px 12px;
    text-align: center;
    font-size: 12px;
    color: var(--el-text-color-placeholder);
  }

  &__foot {
    padding: 6px 12px 8px;
    font-size: 10px;
    text-align: center;
    color: var(--el-text-color-placeholder);
    background: var(--el-fill-color-lighter);
    border-top: 1px dashed var(--el-border-color-lighter);
  }
}
</style>
