<script setup lang="ts">
import { computed, markRaw } from 'vue';
import { useRouter } from 'vue-router';
import type { PaginationProps } from 'element-plus';
import { Calendar, Flag, Link, List, Plus, Star, User } from '@element-plus/icons-vue';
import { RDMS_REQ_PRIORITY_DICT_CODE } from '@/constants/dict';
import DictTag from '@/components/custom/dict-tag.vue';
import { formatDateRange, getExecutionStatusName, getExecutionStatusTagType } from '../shared';
import { projectRequirementStatusRecord } from '../../requirement/shared/requirement-master-data';
import { useTaskPermissions } from '../composables/use-task-permissions';
import IconMdiAccountMultipleOutline from '~icons/mdi/account-multiple-outline';
import IconMdiCheckCircleOutline from '~icons/mdi/check-circle-outline';
import IconMdiCloseCircleOutline from '~icons/mdi/close-circle-outline';
import IconMdiPause from '~icons/mdi/pause';
import IconMdiPencilOutline from '~icons/mdi/pencil-outline';
import IconMdiPlay from '~icons/mdi/play';
import IconMdiRestart from '~icons/mdi/restart';
import IconMdiSync from '~icons/mdi/sync';

defineOptions({ name: 'ProjectTaskExecutionSection' });

type ExecutionStatusFilter = string | null;

type ExecutionViewContextType = 'my' | 'all';

interface Props {
  data: Api.Project.ProjectExecution[];
  loading: boolean;
  pagination: Partial<PaginationProps & Record<string, any>>;
  /** 当前激活的执行 id；跨执行视角时为 null，对应没有任何卡片高亮 */
  activeExecutionId: string | null;
  statusBoard: Api.Project.StatusBoard | null;
  selectedStatus: ExecutionStatusFilter;
  canCreate: boolean;
  ownerOptions: Api.SystemManage.UserSimple[];
  /** 当前执行视角(身份维度):my=我参与的 / all=所有 */
  viewContextType: ExecutionViewContextType;
  /** "我参与的"chip 数字 */
  myCount: number;
  /** "所有"chip 数字 */
  allCount: number;
  /** 快捷过滤计数:逾期执行数(仅"我参与的"视角展示 chip) */
  overdueCount: number;
  /** 快捷过滤计数:本周到期执行数 */
  thisWeekCount: number;
  /** 是否展示视角切换 chip 行;无 project:execution:query 权限时为 false */
  showPerspectiveSwitch: boolean;
}

interface Emits {
  (e: 'select', row: Api.Project.ProjectExecution): void;
  (e: 'status-change', status: ExecutionStatusFilter): void;
  (e: 'create'): void;
  (e: 'edit', row: Api.Project.ProjectExecution): void;
  (e: 'view', row: Api.Project.ProjectExecution): void;
  (e: 'members', row: Api.Project.ProjectExecution): void;
  (e: 'delete', row: Api.Project.ProjectExecution): void;
  (e: 'search'): void;
  (e: 'reset'): void;
  (
    e: 'status-action',
    row: Api.Project.ProjectExecution,
    action: Api.Project.LifecycleAction<Api.Project.ProjectExecutionActionCode> | null
  ): void;
  /** 切换执行视角(身份维度) */
  (e: 'select-perspective', type: ExecutionViewContextType): void;
  /** 切换"逾期/本周到期"快捷过滤(再点已选中 → 传 null 取消) */
  (e: 'due-range-change', range: Api.Project.ProjectExecutionDueRange | null): void;
}

const props = defineProps<Props>();
const emit = defineEmits<Emits>();

const searchModel = defineModel<Api.Project.ProjectExecutionSearchParams>('searchModel', { required: true });

function handleSearch() {
  emit('search');
}

function handleKeywordInput(value: string) {
  searchModel.value.keyword = value.trim() || undefined;
}

function handleKeywordClear() {
  searchModel.value.keyword = undefined;
  handleSearch();
}

function handleOwnerSelect(id: string | null | undefined) {
  searchModel.value.ownerId = id || undefined;
  handleSearch();
}

function handleReset() {
  emit('reset');
}

const router = useRouter();
const { canEditExecution, canDeleteExecution, canSeeExecutionAssigneeEntry } = useTaskPermissions();

const statusChips = computed(() =>
  (props.statusBoard?.items ?? []).map(item => ({
    key: item.statusCode as ExecutionStatusFilter,
    label: item.statusName,
    count: item.count,
    // 状态色调 → 复用业务对象状态色注册表(src/constants/status-tag.ts),保证与右侧执行卡内的 ElTag 同源
    tone: getExecutionStatusTagType(item.statusCode)
  }))
);

// 状态下拉:选中某状态 → 该状态;clearable 清空(value 为 '' / undefined) → null,回到"不限状态"
function handleStatusSelect(value: ExecutionStatusFilter) {
  emit('status-change', value || null);
}

// 跟右侧任务侧 perspectiveCards 保持顺序一致:所有(List) 在前、我参与的(Star) 在后,icon 一致
const perspectiveChips = computed(() => [
  { key: 'all' as ExecutionViewContextType, label: '所有', count: props.allCount, icon: markRaw(List) },
  { key: 'my' as ExecutionViewContextType, label: '我参与的', count: props.myCount, icon: markRaw(Star) }
]);

// "我参与的"视角下的快捷过滤:逾期(标红) / 本周到期。"已完成"复用下方状态 chip,不在此重复
const dueRangeChips = computed(() => [
  { key: 'overdue' as Api.Project.ProjectExecutionDueRange, label: '逾期', count: props.overdueCount, danger: true },
  {
    key: 'thisWeek' as Api.Project.ProjectExecutionDueRange,
    label: '本周到期',
    count: props.thisWeekCount,
    danger: false
  }
]);

// 再点已选中的 chip → 传 null 取消(回到不限截止时间)
function handleDueRangeChipClick(key: Api.Project.ProjectExecutionDueRange) {
  emit('due-range-change', searchModel.value.dueRange === key ? null : key);
}

const totalCount = computed(() => Number(props.pagination.total || 0));
const paginationVisible = computed(() => totalCount.value > 0);

function handlePageChange(page: number) {
  props.pagination['current-change']?.(page);
}

function getProjectRequirementStatusName(code: string | null) {
  if (!code) return '';
  return projectRequirementStatusRecord[code as keyof typeof projectRequirementStatusRecord] ?? code;
}

function handleRequirementClick(row: Api.Project.ProjectExecution) {
  if (!row.projectRequirementId) return;
  router.push({
    path: '/project/project/requirement',
    query: {
      objectId: row.projectId,
      requirementId: row.projectRequirementId
    }
  });
}

interface ExecutionAction {
  key: string;
  tooltip: string;
  icon: object;
  type: 'primary' | 'success' | 'danger' | 'warning';
  onClick: () => void;
}

const STATUS_ACTION_ICON_MAP: Record<string, object> = {
  start: markRaw(IconMdiPlay),
  pause: markRaw(IconMdiPause),
  resume: markRaw(IconMdiRestart),
  cancel: markRaw(IconMdiCloseCircleOutline),
  complete: markRaw(IconMdiCheckCircleOutline)
};

const STATUS_ACTION_TYPE_MAP: Record<string, ExecutionAction['type']> = {
  cancel: 'danger',
  pause: 'warning',
  complete: 'success',
  resume: 'primary',
  start: 'primary'
};

const STATUS_ACTION_ORDER: Record<string, number> = {
  pause: 1,
  cancel: 2,
  complete: 3,
  resume: 4,
  start: 5
};

function createActions(row: Api.Project.ProjectExecution): ExecutionAction[] {
  const actions: ExecutionAction[] = [];

  if (canEditExecution(row)) {
    actions.push({
      key: 'edit',
      tooltip: '编辑',
      icon: markRaw(IconMdiPencilOutline),
      type: 'primary',
      onClick: () => emit('edit', row)
    });
  }

  if (canSeeExecutionAssigneeEntry(row)) {
    actions.push({
      key: 'members',
      tooltip: '协办人',
      icon: markRaw(IconMdiAccountMultipleOutline),
      type: 'primary',
      onClick: () => emit('members', row)
    });
  }

  const sortedActions = [...row.availableActions].sort(
    (a, b) => (STATUS_ACTION_ORDER[a.actionCode] ?? 99) - (STATUS_ACTION_ORDER[b.actionCode] ?? 99)
  );
  sortedActions.forEach(action => {
    actions.push({
      key: action.actionCode,
      tooltip: action.actionName,
      icon: markRaw(STATUS_ACTION_ICON_MAP[action.actionCode] ?? IconMdiSync),
      type: STATUS_ACTION_TYPE_MAP[action.actionCode] ?? 'primary',
      onClick: () => emit('status-action', row, action)
    });
  });

  return actions;
}
</script>

<template>
  <section class="execution-section">
    <header class="execution-section__header">
      <h3 class="execution-section__title">执行池</h3>
      <div
        v-if="showPerspectiveSwitch"
        class="execution-section__perspective"
        role="tablist"
        aria-label="执行视角与快捷过滤切换"
      >
        <ElTooltip
          v-for="item in perspectiveChips"
          :key="item.key"
          :content="item.label"
          placement="top"
          :show-after="120"
        >
          <button
            type="button"
            role="tab"
            class="perspective-compact"
            :class="{ 'is-active': viewContextType === item.key }"
            :aria-label="`${item.label}：${item.count}`"
            :aria-pressed="viewContextType === item.key"
            @click="emit('select-perspective', item.key)"
          >
            <ElIcon><component :is="item.icon" /></ElIcon>
            <span class="perspective-compact__value">{{ item.count }}</span>
          </button>
        </ElTooltip>

        <button
          v-for="chip in viewContextType === 'my' ? dueRangeChips : []"
          :key="chip.key"
          type="button"
          class="perspective-compact"
          :class="{
            'is-active': searchModel.dueRange === chip.key,
            'perspective-compact--danger': chip.danger
          }"
          :aria-pressed="searchModel.dueRange === chip.key"
          @click="handleDueRangeChipClick(chip.key)"
        >
          <span class="perspective-compact__label">{{ chip.label }}</span>
          <span class="perspective-compact__value">{{ chip.count }}</span>
        </button>
      </div>
    </header>

    <div class="execution-section__search-row">
      <ElInput
        :model-value="searchModel.keyword ?? ''"
        class="execution-section__keyword-input"
        placeholder="执行名称"
        @update:model-value="handleKeywordInput"
        @keyup.enter="handleSearch"
      >
        <template #suffix>
          <ElIcon v-if="searchModel.keyword" class="execution-section__search-clear" @click="handleKeywordClear">
            <icon-mdi-close-circle />
          </ElIcon>
        </template>
      </ElInput>

      <ElSelect
        :model-value="searchModel.ownerId ?? null"
        class="execution-section__owner-select"
        placeholder="负责人"
        clearable
        filterable
        @change="handleOwnerSelect"
      >
        <ElOption v-for="item in ownerOptions" :key="item.id" :label="item.nickname" :value="item.id" />
      </ElSelect>
    </div>

    <div class="execution-section__filter-row">
      <ElSelect
        :model-value="selectedStatus"
        class="execution-section__status-select"
        placeholder="状态"
        clearable
        @change="handleStatusSelect"
      >
        <ElOption v-for="item in statusChips" :key="item.key ?? ''" :label="item.label" :value="item.key ?? ''">
          <span class="execution-section__status-option">
            <span class="execution-section__status-option-dot" :data-tone="item.tone" aria-hidden="true" />
            <span class="execution-section__status-option-label">{{ item.label }}</span>
            <span class="execution-section__status-option-count">{{ item.count }}</span>
          </span>
        </ElOption>
      </ElSelect>

      <div class="execution-section__filter-actions">
        <ElTooltip content="重置" placement="top">
          <ElButton link class="execution-section__action-btn" @click="handleReset">
            <icon-mdi-refresh class="text-16px" />
          </ElButton>
        </ElTooltip>
        <ElTooltip content="查询" placement="top">
          <ElButton link type="primary" class="execution-section__action-btn" @click="handleSearch">
            <icon-ic-round-search class="text-16px" />
          </ElButton>
        </ElTooltip>
        <ElTooltip v-if="canCreate" content="新增" placement="top">
          <ElButton link type="primary" class="execution-section__action-btn" @click="emit('create')">
            <ElIcon><Plus /></ElIcon>
          </ElButton>
        </ElTooltip>
      </div>
    </div>

    <ElScrollbar class="execution-section__scrollbar">
      <ElSkeleton v-if="loading" :rows="5" animated />
      <ElEmpty v-else-if="data.length === 0" class="execution-section__empty" description="暂无执行项" />
      <div v-else class="execution-section__list">
        <article
          v-for="row in data"
          :key="row.id"
          class="exec-item"
          :class="{ 'is-active': activeExecutionId === row.id }"
          @click="emit('select', row)"
        >
          <div class="exec-item__top">
            <strong
              class="exec-item__name"
              role="button"
              tabindex="0"
              @click.stop="emit('view', row)"
              @keydown.enter.stop.prevent="emit('view', row)"
            >
              {{ row.executionName || '未命名执行' }}
            </strong>
            <DictTag
              class="exec-item__tag"
              :dict-code="RDMS_REQ_PRIORITY_DICT_CODE"
              :value="row.priority"
              effect="light"
              size="small"
            />
            <ElTag class="exec-item__tag" :type="getExecutionStatusTagType(row.statusCode)" effect="light" size="small">
              {{ getExecutionStatusName(row) }}
            </ElTag>
            <div class="exec-item__actions" @click.stop>
              <ElTooltip v-for="action in createActions(row)" :key="action.key" :content="action.tooltip">
                <ElButton link :type="action.type" class="exec-item__action-btn" @click="action.onClick()">
                  <component :is="action.icon" class="text-14px" />
                </ElButton>
              </ElTooltip>
              <ElTooltip v-if="canDeleteExecution(row)" content="删除">
                <ElButton link type="danger" class="exec-item__action-btn" @click="emit('delete', row)">
                  <icon-mdi-delete-outline class="text-14px" />
                </ElButton>
              </ElTooltip>
            </div>
          </div>
          <div class="exec-item__meta">
            <span>
              <ElIcon><User /></ElIcon>
              {{ row.ownerNickname || '未设置负责人' }}
            </span>
            <span>
              <ElIcon><Flag /></ElIcon>
              计划 {{ formatDateRange(row.plannedStartDate, row.plannedEndDate) }}
            </span>
            <span>
              <ElIcon><Calendar /></ElIcon>
              实际 {{ formatDateRange(row.actualStartDate, row.actualEndDate) }}
            </span>
            <span v-if="row.projectRequirementId && row.projectRequirementName" class="exec-item__requirement">
              <ElIcon><Link /></ElIcon>
              <ElTooltip
                :content="getProjectRequirementStatusName(row.projectRequirementStatusCode)"
                placement="top"
                :show-after="200"
                :disabled="!row.projectRequirementStatusCode"
              >
                <span
                  class="exec-item__requirement-name"
                  role="button"
                  tabindex="0"
                  @click.stop="handleRequirementClick(row)"
                  @keydown.enter.stop.prevent="handleRequirementClick(row)"
                >
                  {{ row.projectRequirementName }}
                </span>
              </ElTooltip>
            </span>
            <span class="exec-item__progress-row">
              <ElProgress class="exec-item__progress" :percentage="row.progressRate" :stroke-width="6" />
            </span>
          </div>
        </article>
      </div>
    </ElScrollbar>

    <div v-if="paginationVisible" class="execution-section__pagination">
      <ElPagination
        size="small"
        background
        layout="prev, pager, next"
        v-bind="pagination"
        @current-change="handlePageChange"
      />
    </div>
  </section>
</template>

<style scoped lang="scss">
.execution-section {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1;
  gap: 8px;
  padding: 12px;
}

.execution-section__header {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.execution-section__title {
  margin: 0;
  color: rgb(15 23 42 / 94%);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.4;
}

.execution-section__count {
  color: var(--el-text-color-secondary);
  font-size: 12.5px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.execution-section__search-row,
.execution-section__filter-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.execution-section__keyword-input {
  flex: 1;
  min-width: 0;
}

.execution-section__search-icon {
  color: var(--el-text-color-placeholder);
}

.execution-section__search-clear {
  color: rgb(192 196 204);
  cursor: pointer;
  transition: color 0.2s ease;
}

.execution-section__search-clear:hover {
  color: rgb(144 147 153);
}

.execution-section__owner-select,
.execution-section__status-select {
  flex: 1;
  min-width: 96px;
}

.execution-section__filter-actions {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  min-width: 96px;
}

.execution-section__filter-actions :deep(.el-button + .el-button) {
  margin-left: 0;
}

:deep(.execution-section__action-btn) {
  width: 28px;
  min-width: 28px;
  height: 28px;
  padding: 0;
}

/* 状态下拉项:色点 + 状态名 + 数量 */
.execution-section__status-option {
  display: flex;
  align-items: center;
  gap: 8px;
}

.execution-section__status-option-dot {
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 50%;
  background-color: var(--el-color-info);
}

.execution-section__status-option-dot[data-tone='primary'] {
  background-color: var(--el-color-primary);
}

.execution-section__status-option-dot[data-tone='success'] {
  background-color: var(--el-color-success);
}

.execution-section__status-option-dot[data-tone='warning'] {
  background-color: var(--el-color-warning);
}

.execution-section__status-option-dot[data-tone='danger'] {
  background-color: var(--el-color-danger);
}

.execution-section__status-option-dot[data-tone='info'] {
  background-color: var(--el-color-info);
}

.execution-section__status-option-label {
  flex: 1;
}

.execution-section__status-option-count {
  color: var(--el-text-color-secondary);
  font-variant-numeric: tabular-nums;
}

/*
 * 视角切换 + 快捷过滤:横排 pill,与右侧任务区视角行同一控件语言。
 * 视角(所有 / 我参与的)始终在,图标 + 文字 + 数字;逾期 / 本周到期仅"我参与的"视角追加,同一行,窄屏自动折行。
 */
.execution-section__perspective {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
}

.perspective-compact {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 26px;
  padding: 0 8px;
  border: 1px solid rgb(226 232 240);
  border-radius: 6px;
  background: #fff;
  color: rgb(100 116 139);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  transition:
    background 0.16s ease,
    border-color 0.16s ease,
    color 0.16s ease;
}

.perspective-compact__label {
  line-height: 1;
}

.perspective-compact:not(.is-active):hover {
  color: var(--el-color-primary);
  border-color: var(--el-color-primary);
}

.perspective-compact.is-active {
  background-color: var(--el-color-primary);
  border-color: var(--el-color-primary);
  color: #fff;
  box-shadow: 0 2px 6px rgb(64 158 255 / 28%);
}

.perspective-compact__value {
  padding: 1px 5px;
  border-radius: 6px;
  background-color: rgb(255 255 255 / 70%);
  font-size: 11px;
}

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

/* 逾期 chip:未选中红字红边,选中红底(与任务区快捷过滤的 danger 款一致) */
.perspective-compact--danger:not(.is-active) {
  color: var(--el-color-danger);
  border-color: rgb(252 165 165 / 70%);
}

.perspective-compact--danger:not(.is-active):hover {
  color: var(--el-color-danger);
  border-color: var(--el-color-danger);
}

.perspective-compact--danger.is-active {
  background-color: var(--el-color-danger);
  border-color: var(--el-color-danger);
  color: #fff;
  box-shadow: 0 2px 6px rgb(245 108 108 / 28%);
}

.perspective-compact--danger.is-active .perspective-compact__value {
  background-color: #fff;
  color: var(--el-color-danger);
}

.execution-status-cell {
  display: flex;
  align-items: center;
  justify-content: space-between;
  min-width: 0;
  min-height: 40px;
  padding: 8px 10px;
  border: 1px solid var(--el-border-color-lighter);
  border-radius: 6px;
  background-color: rgb(248 250 252 / 80%);
  color: rgb(51 65 85 / 96%);
  cursor: pointer;
  transition:
    border-color 0.16s ease,
    background-color 0.16s ease,
    color 0.16s ease;
}

.execution-status-cell:hover {
  border-color: rgb(148 163 184 / 80%);
  background-color: #fff;
}

.execution-status-cell.is-active {
  border-color: var(--el-color-primary);
  background-color: var(--el-color-primary-light-9);
  color: var(--el-color-primary);
}

.execution-status-cell span {
  overflow: hidden;
  min-width: 0;
  font-size: 13px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.execution-status-cell strong {
  flex: 0 0 auto;
  font-size: 16px;
  font-weight: 700;
}

.execution-section__scrollbar {
  flex: 1;
  min-height: 0;
  padding-top: 8px;
  border-top: 1px solid var(--el-border-color-lighter);
}

.execution-section__empty {
  padding: 24px 0;
}

.execution-section__list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding-right: 4px;
}

.exec-item {
  padding: 9px 10px;
  border: 1px solid rgb(226 232 240 / 92%);
  border-radius: 6px;
  background-color: #fff;
  cursor: pointer;
  transition:
    background 0.16s ease,
    border-color 0.16s ease;
}

.exec-item:hover {
  background-color: rgb(243 244 246 / 60%);
  border-color: rgb(148 163 184 / 76%);
}

.exec-item.is-active {
  background-color: var(--el-color-primary-light-9);
  border-color: var(--el-color-primary);
  border-left-width: 3px;
  padding-left: 8px;
}

.exec-item__top {
  display: flex;
  align-items: center;
  gap: 6px;
  min-height: 22px;
}

.exec-item__name {
  overflow: hidden;
  min-width: 0;
  color: rgb(15 23 42 / 94%);
  font-size: 13px;
  font-weight: 600;
  line-height: 1.45;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  transition: color 0.16s ease;
}

.exec-item__name:hover,
.exec-item__name:focus-visible {
  color: var(--el-color-primary);
  outline: none;
}

.exec-item__tag {
  flex: 0 0 auto;
}

.exec-item__actions {
  display: none;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
  margin-left: auto;
}

.exec-item:hover .exec-item__actions {
  display: inline-flex;
}

.exec-item__actions :deep(.el-button + .el-button) {
  margin-left: 0;
}

:deep(.exec-item__action-btn) {
  padding: 2px;
  min-width: auto;
  height: auto;
  line-height: 1;
}

.exec-item__meta {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-top: 6px;
  color: rgb(100 116 139);
  font-size: 11.5px;
  line-height: 1.45;
}

.exec-item__meta span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

.exec-item__progress-row {
  width: 100%;
}
.exec-item__progress {
  flex: 1;
  min-width: 0;
}

.exec-item__requirement {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  overflow: hidden;
}

.exec-item__requirement-name {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--el-color-primary);
  cursor: pointer;
}

.exec-item__requirement-name:hover {
  text-decoration: underline;
}

.execution-section__pagination {
  display: flex;
  justify-content: center;
  padding-top: 4px;
}
</style>
