<script setup lang="ts">
import { computed, nextTick, onActivated, ref, watch } from 'vue';
import { useRouter } from 'vue-router';
import dayjs from 'dayjs';
import { OBJECT_CONTEXT_QUERY_KEY } from '@/constants/object-context';
import { fetchGetMyWorklogWeek, fetchGetTeamWorklogWeek } from '@/service/api';
import { type ECOption, useEcharts } from '@/hooks/common/echarts';
import { getWorkbenchItemColor } from '../composables/use-workbench-colors';
import { useWorkbenchWorklogSignal } from '../composables/use-workbench-worklog-signal';
import {
  type WorkbenchWorklogDistributionItem,
  buildWorkbenchTeamWorklogView,
  buildWorkbenchWeekWorklogView
} from '../homepage';
import WorkbenchModuleCard from './workbench-module-card.vue';

defineOptions({ name: 'WorkbenchMyWeekWorklog' });

interface Props {
  editing?: boolean;
}
withDefaults(defineProps<Props>(), { editing: false });
defineEmits<{ (e: 'hide'): void }>();

const router = useRouter();

const myWeekData = ref<Api.Project.MyWorklogWeekResult | null>(null);
const teamWeekData = ref<Api.Project.TeamWorklogWeekResult | null>(null);

// EP type='week' 默认 firstDayOfWeek=7，从日历点选时返回当周"周日"。
// 我们按 ISO 周（周一-周日）存储；遇到周日 +1 天再 startOf('isoWeek')，避免回退到上一周。
function resolveIsoWeekStart(weekDate: Date | null) {
  if (!weekDate) return null;
  const picked = dayjs(weekDate);
  if (!picked.isValid()) return null;
  const aligned = picked.isoWeekday() === 7 ? picked.add(1, 'day') : picked;
  return aligned.startOf('isoWeek');
}

const weekDateShortcuts = [
  { text: '本周', value: () => dayjs().startOf('isoWeek').toDate() },
  { text: '上周', value: () => dayjs().subtract(1, 'week').startOf('isoWeek').toDate() }
];

const selectedWeekDate = ref<Date | null>(dayjs().startOf('isoWeek').toDate());

const selectedWeekStart = computed(() => {
  const aligned = resolveIsoWeekStart(selectedWeekDate.value);
  return aligned ? aligned.format('YYYY-MM-DD') : '';
});

// 周切换必须重拉，不能被"并发守卫"拦掉，故不走 useWorkbenchRefresh：
// 自管 loading + 请求序号，旧响应（慢请求落后于新一次切周）直接丢弃
const loading = ref(false);
let requestSeq = 0;

async function loadWorklogWeek() {
  const weekStart = selectedWeekStart.value;
  if (!weekStart) return;

  requestSeq += 1;
  const seq = requestSeq;
  loading.value = true;
  try {
    const [myResult, teamResult] = await Promise.all([
      fetchGetMyWorklogWeek({ weekStart }),
      fetchGetTeamWorklogWeek({ weekStart })
    ]);

    if (seq !== requestSeq) return;
    myWeekData.value = myResult.error || !myResult.data ? null : myResult.data;
    teamWeekData.value = teamResult.error || !teamResult.data ? null : teamResult.data;
  } finally {
    if (seq === requestSeq) {
      loading.value = false;
    }
  }
}

function refresh() {
  loadWorklogWeek();
}

type TabKey = 'my' | 'team';
const activeTab = ref<TabKey>('my');

// 周切换（含初始）拉取两 tab 数据；竞态由 loadWorklogWeek 内请求序号兜底
watch(selectedWeekStart, loadWorklogWeek, { immediate: true });

// 同一工作台内（如 todo 面板）弹层填报工时不切路由、不触发 onActivated，靠信号驱动重拉当前周
const { revision: worklogRevision } = useWorkbenchWorklogSignal();
watch(worklogRevision, loadWorklogWeek);

// 工作台路由 keepAlive：切回时组件不重挂载，immediate watch 不再触发。
// 每次激活归位到当前周并重拉；首次激活与挂载同拍（上面 immediate 已拉过），跳过避免双发
let activatedOnce = false;
onActivated(() => {
  if (!activatedOnce) {
    activatedOnce = true;
    return;
  }
  const currentWeekDate = dayjs().startOf('isoWeek');
  if (selectedWeekStart.value === currentWeekDate.format('YYYY-MM-DD')) {
    // 周未变时 watch 不会触发，手动重拉取最新填报
    loadWorklogWeek();
  } else {
    selectedWeekDate.value = currentWeekDate.toDate();
  }
});

// ============ 我的工时 ============

const myView = computed(() => (myWeekData.value ? buildWorkbenchWeekWorklogView(myWeekData.value) : null));

const isCurrentWeek = computed(() => selectedWeekStart.value === dayjs().startOf('isoWeek').format('YYYY-MM-DD'));

const totalLabel = computed(() => (isCurrentWeek.value ? '累计' : '该周累计'));

const deltaInfo = computed(() => {
  if (!myView.value) return null;
  const { delta, completionRate } = myView.value;
  if (isCurrentWeek.value) {
    if (delta >= 0) return { text: `领先目标 +${delta}h`, tone: 'success' as const };
    return { text: `落后目标 ${delta}h`, tone: 'danger' as const };
  }
  return { text: `达成 ${completionRate}%`, tone: completionRate >= 100 ? ('success' as const) : ('muted' as const) };
});

const DAY_BAR_COLOR = '#409EFF';

interface DistributionRow extends WorkbenchWorklogDistributionItem {
  color: string;
}

const distributionRows = computed<DistributionRow[]>(() => {
  const list = myView.value?.distribution ?? [];
  return list.map(item => ({ ...item, color: getWorkbenchItemColor(item.key, item.kind) }));
});

function handleDistributionClick(item: WorkbenchWorklogDistributionItem) {
  if (item.kind === 'project' && item.projectId) {
    router.push({
      path: '/project/project/execution',
      query: { [OBJECT_CONTEXT_QUERY_KEY]: item.projectId }
    });
    return;
  }
  if (item.kind === 'personal') {
    router.push({ path: '/personal-center/my-item' });
  }
}

function buildPieOption(): ECOption {
  const list = distributionRows.value;
  return {
    tooltip: { trigger: 'item', formatter: '{b}: {c}h ({d}%)' },
    series: [
      {
        type: 'pie',
        radius: ['38%', '58%'],
        center: ['50%', '52%'],
        avoidLabelOverlap: true,
        minAngle: 6,
        label: {
          show: true,
          formatter: (params: any) => `${params.name}\n${params.percent}%`,
          color: '#475569',
          fontSize: 11,
          lineHeight: 14
        },
        labelLine: {
          show: true,
          length: 6,
          length2: 8,
          smooth: false
        },
        emphasis: {
          scale: true,
          scaleSize: 4,
          label: { fontWeight: 700 }
        },
        data: list.map(item => ({
          name: item.label,
          value: item.hours,
          itemStyle: { color: item.color },
          cursor: item.kind === 'other' ? 'default' : 'pointer'
        }))
      }
    ]
  };
}

function buildMyBarOption(): ECOption {
  const v = myView.value;
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'shadow' },
      formatter: (rawParams: any) => {
        const params = Array.isArray(rawParams) ? rawParams : [rawParams];
        const dayName = params[0]?.axisValue ?? '';
        const total = Number(params[0]?.value ?? 0);
        return `周${dayName}：${total}h`;
      }
    },
    grid: { left: 28, right: 8, top: 16, bottom: 24, containLabel: false },
    xAxis: {
      type: 'category',
      data: v?.visibleDayLabels ?? ['一', '二', '三', '四', '五'],
      axisTick: { show: false },
      axisLine: { lineStyle: { color: '#e5e7eb' } },
      axisLabel: { color: '#6b7280', fontSize: 11 }
    },
    yAxis: {
      type: 'value',
      splitLine: { lineStyle: { color: '#f3f4f6' } },
      axisLabel: { color: '#9ca3af', fontSize: 10 }
    },
    series: [
      {
        name: '每日工时',
        type: 'bar',
        barWidth: 18,
        data: v?.visibleDailyHours ?? [],
        itemStyle: { color: DAY_BAR_COLOR, borderRadius: [2, 2, 0, 0] }
      }
    ]
  };
}

const { domRef: pieRef, updateOptions: updatePie } = useEcharts(buildPieOption, {
  onRender(chart) {
    chart.on('click', (params: any) => {
      const item = myView.value?.distribution[params.dataIndex];
      if (item) handleDistributionClick(item);
    });
  }
});

const { domRef: myBarRef, updateOptions: updateMyBar } = useEcharts(buildMyBarOption, { onRender() {} });

// ============ 团队工时 ============

const teamView = computed(() => (teamWeekData.value ? buildWorkbenchTeamWorklogView(teamWeekData.value) : null));

function buildTeamBarOption(): ECOption {
  const v = teamView.value;
  if (!v) return {};
  return {
    tooltip: {
      trigger: 'axis',
      axisPointer: { type: 'shadow' },
      formatter: (rawParams: any) => {
        const params = Array.isArray(rawParams) ? rawParams : [rawParams];
        if (!params.length) return '';
        const name = params[0].axisValue as string;
        const totalHours = Number(params.find((p: any) => p.seriesName === '总工时')?.value ?? 0);
        const overtimeHours = Number(params.find((p: any) => p.seriesName === '学习工时')?.value ?? 0);
        return [
          `<div style="font-weight:600;margin-bottom:4px">${name}</div>`,
          `总工时：${totalHours}h`,
          `学习工时：${overtimeHours}h`
        ].join('<br/>');
      }
    },
    grid: { left: 32, right: 12, top: 16, bottom: 24, containLabel: false },
    xAxis: {
      type: 'category',
      data: v.members.map(m => m.memberName),
      axisTick: { show: false },
      axisLine: { lineStyle: { color: '#e5e7eb' } },
      axisLabel: { color: '#6b7280', fontSize: 11 }
    },
    yAxis: {
      type: 'value',
      splitLine: { lineStyle: { color: '#f3f4f6' } },
      axisLabel: { color: '#9ca3af', fontSize: 10, formatter: '{value}h' }
    },
    series: [
      {
        name: '总工时',
        type: 'bar',
        barWidth: 16,
        data: v.members.map(member => member.totalHours),
        itemStyle: {
          color: '#409EFF',
          borderRadius: [3, 3, 0, 0]
        }
      },
      {
        name: '学习工时',
        type: 'bar',
        barWidth: 16,
        data: v.members.map(member => member.overtimeHours),
        itemStyle: {
          color: '#F59E0B',
          borderRadius: [3, 3, 0, 0]
        }
      }
    ]
  };
}

const { domRef: teamBarRef, updateOptions: updateTeamBar } = useEcharts(buildTeamBarOption, { onRender() {} });

// ============ 数据 / 切换变化时刷新 echarts ============

watch(myView, () => {
  updatePie((_, factory) => factory());
  updateMyBar((_, factory) => factory());
});

watch(teamView, () => {
  updateTeamBar((_, factory) => factory());
});

// 切 tab 后等 v-show 容器渲染再触发 echarts 重画（避免 display:none → block 切换时尺寸残留为 0）
watch(activeTab, async tab => {
  await nextTick();
  if (tab === 'my') {
    updatePie((_, factory) => factory());
    updateMyBar((_, factory) => factory());
  } else {
    updateTeamBar((_, factory) => factory());
  }
});
</script>

<template>
  <WorkbenchModuleCard
    v-loading="loading"
    title="工时"
    icon="mdi:timer-outline"
    :editing="editing"
    @hide="$emit('hide')"
    @refresh="refresh"
  >
    <div class="ww-tabbar">
      <ElTabs v-model="activeTab" class="ww-tabs">
        <ElTabPane label="我的工时" name="my" />
        <ElTabPane label="团队工时" name="team" />
      </ElTabs>
      <ElDatePicker
        v-model="selectedWeekDate"
        type="week"
        format="YYYY[年第]ww[周]"
        placeholder="选择周次"
        :shortcuts="weekDateShortcuts"
        :clearable="false"
        size="small"
        class="ww-week-picker"
      />
    </div>

    <!-- ============ 我的工时 tab ============ -->
    <div v-show="activeTab === 'my'" class="ww-tab-content">
      <template v-if="myView">
        <div class="ww-headline">
          <div class="ww-section-title">
            <SvgIcon icon="mdi:chart-donut" class="ww-section-icon" />
            <span>工时分布</span>
          </div>
          <div class="ww-section-title">
            <SvgIcon icon="mdi:calendar-week" class="ww-section-icon" />
            <span>每日工时</span>
            <ElTooltip
              content="接口返回周一~周日逐日工时；按周填报时仅均摊到工作日，周末单天工时保留在周末当天展示"
              placement="top"
            >
              <span class="ww-section-info-wrap">
                <SvgIcon icon="mdi:information-outline" class="ww-section-info" />
              </span>
            </ElTooltip>
          </div>
        </div>

        <div class="ww-grid">
          <div class="ww-block">
            <div class="ww-pie-wrap">
              <div ref="pieRef" class="ww-pie" />
            </div>
          </div>

          <div class="ww-block">
            <div ref="myBarRef" class="ww-bar" />
          </div>
        </div>

        <div class="ww-footer">
          <span>
            {{ totalLabel }}
            <b>{{ myView.totalHours }}h</b>
            / {{ myView.target }}h
          </span>
          <span v-if="deltaInfo" :class="`ww-footer__delta is-${deltaInfo.tone}`">{{ deltaInfo.text }}</span>
        </div>
      </template>
      <ElEmpty v-else description="该周无工时数据" :image-size="60" />
    </div>

    <!-- ============ 团队工时 tab ============ -->
    <div v-show="activeTab === 'team'" class="ww-tab-content">
      <template v-if="teamView">
        <div class="tw-kpis">
          <div class="tw-kpi">
            <span class="tw-kpi__label">填报率</span>
            <span class="tw-kpi__value">
              {{ teamView.fillRate }}
              <span class="tw-kpi__unit">%</span>
            </span>
            <span class="tw-kpi__sub">{{ teamView.totalHours }}h / {{ teamView.expectedTotalHours }}h</span>
          </div>
          <div class="tw-kpi">
            <span class="tw-kpi__label">团队均值</span>
            <span class="tw-kpi__value">
              {{ teamView.averageHours }}
              <span class="tw-kpi__unit">h</span>
            </span>
            <span class="tw-kpi__sub">{{ teamView.members.length }} 人</span>
          </div>
          <div class="tw-kpi">
            <span class="tw-kpi__label">偏低</span>
            <span class="tw-kpi__value" :class="{ 'is-danger': teamView.lowCount > 0 }">
              {{ teamView.lowCount }}
              <span class="tw-kpi__unit">人</span>
            </span>
            <span class="tw-kpi__sub">低于均值 80%</span>
          </div>
        </div>

        <div ref="teamBarRef" class="tw-bar" />

        <div v-if="teamView.lowest && teamView.highest" class="tw-footer">
          <span>
            <SvgIcon icon="mdi:arrow-down-bold-circle-outline" class="tw-footer__icon is-danger" />
            最低
            <b>{{ teamView.lowest.memberName }}</b>
            {{ teamView.lowest.hours }}h
          </span>
          <span class="tw-footer__sep">·</span>
          <span>
            <SvgIcon icon="mdi:arrow-up-bold-circle-outline" class="tw-footer__icon is-warn" />
            最高
            <b>{{ teamView.highest.memberName }}</b>
            {{ teamView.highest.hours }}h
          </span>
        </div>
      </template>
      <ElEmpty v-else description="该周无团队工时数据" :image-size="60" />
    </div>
  </WorkbenchModuleCard>
</template>

<style scoped>
/* ============ 顶部 tab + 周选择器 ============ */
.ww-tabbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--el-border-color-lighter);
  flex-shrink: 0;
}

/* tab 内容区填充剩余高度：flex 列布局，图表区自适应撑满，不写死高度、不内部滚动 */
.ww-tab-content {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.ww-tab-content :deep(.el-empty) {
  margin: auto;
}
.ww-tabs {
  flex: 1;
  min-width: 0;
}
.ww-tabs :deep(.el-tabs__header) {
  margin: 0;
}
.ww-tabs :deep(.el-tabs__nav-wrap::after) {
  display: none;
}
.ww-week-picker {
  width: 180px;
  flex-shrink: 0;
  margin-bottom: 8px;
}

/* ============ 我的工时 ============ */
.ww-headline {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: 16px;
  margin-bottom: 10px;
  flex-shrink: 0;
}

.ww-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px;
  flex: 1;
  min-height: 0;
}
@media (width <= 520px) {
  .ww-grid {
    grid-template-columns: 1fr;
  }
  .ww-headline {
    grid-template-columns: 1fr;
  }
}

.ww-block {
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
}

.ww-section-title {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--el-text-color-secondary);
  min-width: 0;
}
.ww-section-icon {
  font-size: 14px;
  color: var(--el-text-color-placeholder);
  flex-shrink: 0;
}
.ww-section-info-wrap {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  cursor: help;
}

.ww-section-info {
  font-size: 13px;
  color: var(--el-text-color-placeholder);
}

.ww-pie-wrap {
  position: relative;
  width: 100%;
  flex: 1;
  min-height: 0;
}
.ww-pie {
  width: 100%;
  height: 100%;
}

.ww-bar {
  width: 100%;
  flex: 1;
  min-height: 0;
}

.ww-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--el-border-color-lighter);
  font-size: 13px;
  flex-shrink: 0;
}
.ww-footer b {
  font-weight: 700;
}
.ww-footer__delta {
  font-weight: 600;
}
.ww-footer__delta.is-success {
  color: var(--el-color-success);
}
.ww-footer__delta.is-danger {
  color: var(--el-color-danger);
}
.ww-footer__delta.is-muted {
  color: var(--el-text-color-secondary);
}

/* ============ 团队工时 ============ */
.tw-kpis {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 12px;
  flex-shrink: 0;
}
.tw-kpi {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 12px;
  background: var(--el-fill-color-lighter);
  border-radius: 8px;
  min-width: 0;
}
.tw-kpi__label {
  font-size: 11px;
  color: var(--el-text-color-secondary);
}
.tw-kpi__value {
  font-size: 20px;
  font-weight: 700;
  color: var(--el-text-color-primary);
  line-height: 1.2;
}
.tw-kpi__value.is-danger {
  color: var(--el-color-danger);
}
.tw-kpi__unit {
  font-size: 12px;
  font-weight: 500;
  color: var(--el-text-color-secondary);
  margin-left: 2px;
}
.tw-kpi__sub {
  font-size: 11px;
  color: var(--el-text-color-placeholder);
}

.tw-bar {
  width: 100%;
  flex: 1;
  min-height: 0;
}

.tw-footer {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  font-size: 12px;
  color: var(--el-text-color-secondary);
  flex-shrink: 0;
}
.tw-footer b {
  color: var(--el-text-color-primary);
  font-weight: 600;
  margin: 0 2px;
}
.tw-footer__icon {
  vertical-align: -2px;
  margin-right: 2px;
  font-size: 13px;
}
.tw-footer__icon.is-danger {
  color: var(--el-color-danger);
}
.tw-footer__icon.is-warn {
  color: var(--el-color-warning);
}
.tw-footer__sep {
  color: var(--el-text-color-placeholder);
}

@media (width <= 520px) {
  .tw-kpis {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
</style>
