<script setup lang="tsx">
import { computed, markRaw, nextTick, onActivated, reactive, ref } from 'vue';
import type { TableInstance } from 'element-plus';
import { ElButton, ElMessageBox, ElTag, ElTooltip } from 'element-plus';
import { useBoolean } from '@sa/hooks';
import {
  fetchBatchDeletePersonalItems,
  fetchBindPersonalItemsToExecution,
  fetchChangePersonalItemStatus,
  fetchDeletePersonalItem,
  fetchGetPersonalItemDetail,
  fetchGetPersonalItemPage
} from '@/service/api';
import { useAuthStore } from '@/store/modules/auth';
import { useUIPaginatedTable } from '@/hooks/common/table';
import PersonalItemBindExecutionDialog from './modules/personal-item-bind-execution-dialog.vue';
import PersonalItemDetailDialog from './modules/personal-item-detail-dialog.vue';
import PersonalItemOperateDialog from './modules/personal-item-operate-dialog.vue';
import PersonalItemSearch from './modules/personal-item-search.vue';
import PersonalItemStatusActionDialog from './modules/personal-item-status-action-dialog.vue';
import {
  formatPersonalItemDateRange,
  formatPersonalItemDateTime,
  formatPersonalItemOwnerName,
  formatPersonalItemProgress,
  getPersonalItemStatusLabel,
  resolvePersonalItemStatusTagType
} from './modules/personal-item-shared';
import IconMdiCheckCircleOutline from '~icons/mdi/check-circle-outline';
import IconMdiClipboardEditOutline from '~icons/mdi/clipboard-edit-outline';
import IconMdiCloseCircleOutline from '~icons/mdi/close-circle-outline';
import IconMdiDeleteOutline from '~icons/mdi/delete-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: 'MyItem' });

type DetailTab = 'worklog';
type PersonalItemOperateType = UI.TableOperateType | 'view';

interface PersonalItemRowAction {
  key: string;
  tooltip: string;
  icon: object;
  type: 'primary' | 'success' | 'warning' | 'danger';
  disabled?: boolean;
  onClick: () => void | Promise<void>;
}

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

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

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

const authStore = useAuthStore();
const currentUserId = computed(() => {
  const rawUserId = authStore.userInfo.userId;

  return rawUserId ? String(rawUserId) : '';
});

function getInitSearchParams(): Api.PersonalItem.PersonalItemSearchParams {
  return {
    pageNo: 1,
    pageSize: 10,
    keyword: undefined,
    ownerId: currentUserId.value || undefined,
    statusCode: undefined,
    updateTime: undefined
  };
}

function transformPageResult(
  response: Awaited<ReturnType<typeof fetchGetPersonalItemPage>>,
  pageNo: number,
  pageSize: number
) {
  if (!response.error) {
    return {
      data: response.data.list,
      pageNum: pageNo,
      pageSize,
      total: response.data.total
    };
  }

  return {
    data: [],
    pageNum: pageNo,
    pageSize,
    total: 0
  };
}

const searchParams = reactive(getInitSearchParams());
const tableRef = ref<TableInstance>();
const checkedRowIds = ref<string[]>([]);
const bindExecutionSubmitting = ref(false);

const selectedCount = computed(() => checkedRowIds.value.length);

const { columns, columnChecks, data, loading, getDataByPage, mobilePagination } = useUIPaginatedTable({
  paginationProps: {
    currentPage: searchParams.pageNo,
    pageSize: searchParams.pageSize
  },
  api: () => fetchGetPersonalItemPage(searchParams),
  transform: response => transformPageResult(response, searchParams.pageNo ?? 1, searchParams.pageSize ?? 10),
  onPaginationParamsChange: params => {
    searchParams.pageNo = params.currentPage ?? 1;
    searchParams.pageSize = params.pageSize ?? 10;
  },
  columns: () => [
    { prop: 'selection', type: 'selection', width: 48 },
    { prop: 'index', type: 'index', label: '序号', width: 64 },
    {
      prop: 'taskTitle',
      label: '事项标题',
      minWidth: 260,
      showOverflowTooltip: true
    },
    {
      prop: 'ownerName',
      label: '负责人',
      minWidth: 140,
      formatter: row => formatPersonalItemOwnerName(row)
    },
    {
      prop: 'statusCode',
      label: '状态',
      width: 120,
      align: 'center',
      formatter: row => (
        <ElTag type={resolvePersonalItemStatusTagType(row.statusCode)}>
          {getPersonalItemStatusLabel(row.statusCode)}
        </ElTag>
      )
    },
    {
      prop: 'progressRate',
      label: '进度',
      width: 100,
      align: 'center',
      formatter: row => formatPersonalItemProgress(row.progressRate)
    },
    {
      prop: 'plannedDateRange',
      label: '计划日期',
      minWidth: 220,
      formatter: row => formatPersonalItemDateRange(row.plannedStartDate, row.plannedEndDate)
    },
    {
      prop: 'actualDateRange',
      label: '实际日期',
      minWidth: 220,
      formatter: row => formatPersonalItemDateRange(row.actualStartDate, row.actualEndDate)
    },
    {
      prop: 'updateTime',
      label: '最近更新',
      minWidth: 180,
      formatter: row => formatPersonalItemDateTime(row.updateTime)
    },
    {
      prop: 'operate',
      label: '操作',
      width: 240,
      align: 'center',
      fixed: 'right',
      formatter: row => renderRowActions(row)
    }
  ]
});

const { bool: operateVisible, setTrue: openOperateDialog, setFalse: closeOperateDialog } = useBoolean();
const { bool: detailVisible, setTrue: openDetailDialog } = useBoolean();
const {
  bool: bindExecutionVisible,
  setTrue: openBindExecutionDialog,
  setFalse: closeBindExecutionDialog
} = useBoolean();
const { bool: statusActionVisible, setTrue: openStatusActionDialog, setFalse: closeStatusActionDialog } = useBoolean();

const operateType = ref<PersonalItemOperateType>('add');
const editingData = ref<Api.PersonalItem.PersonalItem | null>(null);
const detailData = ref<Api.PersonalItem.PersonalItem | null>(null);
const detailDefaultTab = ref<DetailTab>('worklog');
const currentStatusAction = ref<Api.PersonalItem.PersonalItemLifecycleAction | null>(null);
const currentStatusItem = ref<Api.PersonalItem.PersonalItem | null>(null);

async function openDetail(row: Api.PersonalItem.PersonalItem, defaultTab: DetailTab = 'worklog') {
  const { error, data: latestDetail } = await fetchGetPersonalItemDetail(row.id);

  detailData.value = error || !latestDetail ? row : latestDetail;
  detailDefaultTab.value = defaultTab;
  openDetailDialog();
}

function openView(row: Api.PersonalItem.PersonalItem) {
  operateType.value = 'view';
  editingData.value = row;
  openOperateDialog();
}

// function createLifecycleAction(
//   fallback: {
//     key: string;
//     tooltip: string;
//     icon: object;
//     type: PersonalItemRowAction['type'];
//     actionCode: string;
//   },
//   action: Api.PersonalItem.PersonalItemLifecycleAction | null
// ): PersonalItemRowAction {
//   return {
//     key: fallback.key,
//     tooltip: action?.actionName ?? fallback.tooltip,
//     icon: fallback.icon,
//     type: fallback.type,
//     disabled: !action,
//     onClick: async () =>
//       handleStatusAction(currentStatusItem.value!, {
//         actionCode: action?.actionCode ?? fallback.actionCode,
//         actionName: action?.actionName ?? fallback.tooltip,
//         needReason: action?.needReason ?? false
//       })
//   };
// }

function buildRowActions(row: Api.PersonalItem.PersonalItem): PersonalItemRowAction[] {
  currentStatusItem.value = row;

  const isCompleted = row.statusCode === 'completed';
  const rawLifecycleActions = [...(row.availableActions ?? [])];
  const pauseAction = rawLifecycleActions.find(action => action.actionCode === 'pause') ?? null;
  const completeAction = rawLifecycleActions.find(action => action.actionCode === 'complete') ?? null;

  const lifecycleActions = rawLifecycleActions
    .filter(action => !['pause', 'cancel', 'complete'].includes(action.actionCode))
    .sort(
      (left, right) => (lifecycleActionOrder[left.actionCode] ?? 99) - (lifecycleActionOrder[right.actionCode] ?? 99)
    )
    .map(action => ({
      key: `status-${action.actionCode}`,
      tooltip: action.actionName,
      icon: markRaw(lifecycleActionIconMap[action.actionCode] ?? IconMdiSync),
      type: lifecycleActionTypeMap[action.actionCode] ?? 'primary',
      onClick: async () => handleStatusAction(row, action)
    }));

  return [
    {
      key: 'worklog',
      tooltip: '填报',
      icon: markRaw(IconMdiClipboardEditOutline),
      type: 'primary',
      onClick: async () => openDetail(row, 'worklog')
    },
    ...(!isCompleted
      ? ([
          {
            key: 'edit',
            tooltip: '编辑',
            icon: markRaw(IconMdiPencilOutline),
            type: 'primary',
            onClick: async () => {
              operateType.value = 'edit';
              editingData.value = row;
              openOperateDialog();
            }
          }
        ] satisfies PersonalItemRowAction[])
      : []),
    ...(!isCompleted
      ? ([
          {
            key: 'delete',
            tooltip: '删除',
            icon: markRaw(IconMdiDeleteOutline),
            type: 'danger',
            onClick: async () => handleDelete(row)
          },
          {
            key: 'status-pause',
            tooltip: pauseAction?.actionName ?? '暂停',
            icon: markRaw(IconMdiPause),
            type: 'warning',
            disabled: !pauseAction,
            onClick: async () =>
              handleStatusAction(row, {
                actionCode: pauseAction?.actionCode ?? 'pause',
                actionName: pauseAction?.actionName ?? '暂停',
                needReason: pauseAction?.needReason ?? false
              })
          }
        ] satisfies PersonalItemRowAction[])
      : []),
    // {
    //   key: 'status-cancel',
    //   tooltip: cancelAction?.actionName ?? '取消',
    //   icon: markRaw(IconMdiCloseCircleOutline),
    //   type: 'danger',
    //   disabled: !cancelAction,
    //   onClick: async () =>
    //     handleStatusAction(row, {
    //       actionCode: cancelAction?.actionCode ?? 'cancel',
    //       actionName: cancelAction?.actionName ?? '取消',
    //       needReason: cancelAction?.needReason ?? false
    //     })
    // },
    ...lifecycleActions,
    ...(!isCompleted
      ? ([
          {
            key: 'status-complete',
            tooltip: completeAction?.actionName ?? '完成',
            icon: markRaw(IconMdiCheckCircleOutline),
            type: 'success',
            disabled: !completeAction,
            onClick: async () =>
              handleStatusAction(row, {
                actionCode: completeAction?.actionCode ?? 'complete',
                actionName: completeAction?.actionName ?? '完成',
                needReason: completeAction?.needReason ?? false
              })
          }
        ] satisfies PersonalItemRowAction[])
      : [])
  ];
}

function renderRowActions(row: Api.PersonalItem.PersonalItem) {
  return (
    <div class="personal-item-row-actions" onClick={event => event.stopPropagation()}>
      {buildRowActions(row).map(action => {
        const Icon = action.icon as any;

        return (
          <ElTooltip key={action.key} content={action.tooltip}>
            <span class="inline-flex">
              <ElButton
                link
                type={action.type}
                class="personal-item-row-action-btn"
                disabled={action.disabled}
                onClick={event => {
                  event.stopPropagation();
                  if (action.disabled) {
                    return;
                  }
                  action.onClick();
                }}
              >
                <Icon class="text-15px" />
              </ElButton>
            </span>
          </ElTooltip>
        );
      })}
    </div>
  );
}

function openAdd() {
  operateType.value = 'add';
  editingData.value = null;
  openOperateDialog();
}

function handleSelectionChange(rows: Api.PersonalItem.PersonalItem[]) {
  checkedRowIds.value = rows.map(item => item.id);
}

function resolveReloadPageAfterRemove() {
  const currentPage = searchParams.pageNo ?? 1;

  if (currentPage > 1 && data.value.length > 0 && checkedRowIds.value.length >= data.value.length) {
    return currentPage - 1;
  }

  return currentPage;
}

async function reloadTable(page = searchParams.pageNo ?? 1) {
  checkedRowIds.value = [];
  await getDataByPage(page);
  await nextTick();
  tableRef.value?.clearSelection();
}

function resetSearchParams() {
  Object.assign(searchParams, getInitSearchParams());
  reloadTable(1);
}

function handleSearch() {
  reloadTable(1);
}

function handleSubmitted() {
  closeOperateDialog();
  reloadTable(searchParams.pageNo ?? 1);
}

function handleDetailChanged(latestItem: Api.PersonalItem.PersonalItem) {
  detailData.value = latestItem;

  const targetIndex = data.value.findIndex(item => item.id === latestItem.id);

  if (targetIndex >= 0) {
    data.value.splice(targetIndex, 1, latestItem);
  }
}

function handleStatusAction(row: Api.PersonalItem.PersonalItem, action: Api.PersonalItem.PersonalItemLifecycleAction) {
  currentStatusItem.value = row;
  currentStatusAction.value = action;
  openStatusActionDialog();
}

async function handleStatusActionSubmit(reason: string | null) {
  if (!currentStatusItem.value || !currentStatusAction.value) {
    return;
  }

  const { error } = await fetchChangePersonalItemStatus(currentStatusItem.value.id, {
    actionCode: currentStatusAction.value.actionCode,
    reason
  });

  if (error) {
    return;
  }

  closeStatusActionDialog();
  window.$message?.success(`${currentStatusAction.value.actionName}成功`);
  await reloadTable(searchParams.pageNo ?? 1);
}

async function handleDelete(row: Api.PersonalItem.PersonalItem) {
  try {
    await ElMessageBox.confirm(`确定删除我的事项“${row.taskTitle}”吗？`, '删除确认', {
      type: 'warning',
      confirmButtonText: '确定',
      cancelButtonText: '取消'
    });
  } catch {
    return;
  }

  const { error } = await fetchDeletePersonalItem(row.id);

  if (error) {
    return;
  }

  window.$message?.success('删除成功');
  await reloadTable(searchParams.pageNo ?? 1);
}

async function handleBatchDelete() {
  if (!checkedRowIds.value.length) {
    window.$message?.warning('请先选择我的事项');
    return;
  }

  try {
    await ElMessageBox.confirm(`确定删除选中的 ${selectedCount.value} 条我的事项吗？`, '删除确认', {
      type: 'warning',
      confirmButtonText: '确定',
      cancelButtonText: '取消'
    });
  } catch {
    return;
  }

  const targetPage = resolveReloadPageAfterRemove();
  const { error } = await fetchBatchDeletePersonalItems({ ids: [...checkedRowIds.value] });

  if (error) {
    return;
  }

  window.$message?.success('批量删除成功');
  await reloadTable(targetPage);
}

function handleOpenBindExecution() {
  if (!checkedRowIds.value.length) {
    window.$message?.warning('请先选择我的事项');
    return;
  }

  openBindExecutionDialog();
}

async function handleBindExecutionSubmit(payload: { executionId: string }) {
  bindExecutionSubmitting.value = true;

  const targetPage = resolveReloadPageAfterRemove();
  const { error } = await fetchBindPersonalItemsToExecution({
    ids: [...checkedRowIds.value],
    executionId: payload.executionId
  });

  bindExecutionSubmitting.value = false;

  if (error) {
    return;
  }

  closeBindExecutionDialog();
  window.$message?.success('批量关联执行成功');
  await reloadTable(targetPage);
}

onActivated(() => {
  searchParams.ownerId = currentUserId.value || undefined;
  reloadTable(searchParams.pageNo ?? 1);
});
</script>

<template>
  <div class="flex-col-stretch gap-16px overflow-hidden">
    <PersonalItemSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />

    <ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
      <template #header>
        <div class="flex items-center justify-between gap-12px">
          <div class="flex items-center gap-10px">
            <p>我的事项</p>
            <ElTag effect="plain">{{ mobilePagination.total || data.length }}</ElTag>
          </div>
          <TableHeaderOperation v-model:columns="columnChecks" :loading="loading" @refresh="reloadTable">
            <template #default>
              <ElButton plain type="danger" :disabled="selectedCount === 0" @click="handleBatchDelete">
                <template #icon>
                  <icon-ic-round-delete class="text-icon" />
                </template>
                批量删除
              </ElButton>
              <ElButton plain :disabled="selectedCount === 0" @click="handleOpenBindExecution">
                <template #icon>
                  <icon-mdi-link-variant class="text-icon" />
                </template>
                批量关联执行
              </ElButton>
              <ElButton plain type="primary" @click="openAdd">
                <template #icon>
                  <icon-ic-round-plus class="text-icon" />
                </template>
                新增
              </ElButton>
            </template>
          </TableHeaderOperation>
        </div>
      </template>

      <div class="flex-1">
        <ElTable
          ref="tableRef"
          v-loading="loading"
          height="100%"
          border
          row-key="id"
          :data="data"
          @selection-change="handleSelectionChange"
        >
          <template v-for="col in columns" :key="String(col.prop)">
            <ElTableColumn v-if="col.prop === 'taskTitle'" v-bind="col">
              <template #default="{ row }">
                <ElButton link type="primary" class="personal-item-title-link" @click.stop="openView(row)">
                  {{ row.taskTitle || '--' }}
                </ElButton>
              </template>
            </ElTableColumn>
            <ElTableColumn v-else v-bind="col" />
          </template>
        </ElTable>
      </div>

      <div class="mt-20px flex justify-end">
        <ElPagination
          v-if="mobilePagination.total"
          layout="total,prev,pager,next,sizes"
          v-bind="mobilePagination"
          @current-change="mobilePagination['current-change']"
          @size-change="mobilePagination['size-change']"
        />
      </div>
    </ElCard>

    <PersonalItemOperateDialog
      v-model:visible="operateVisible"
      :operate-type="operateType"
      :row-data="editingData"
      @submitted="handleSubmitted"
    />

    <PersonalItemDetailDialog
      v-model:visible="detailVisible"
      :row-data="detailData"
      :default-tab="detailDefaultTab"
      @changed="handleDetailChanged"
    />

    <PersonalItemBindExecutionDialog
      v-model:visible="bindExecutionVisible"
      :selected-count="selectedCount"
      :submit-loading="bindExecutionSubmitting"
      @submit="handleBindExecutionSubmit"
    />

    <PersonalItemStatusActionDialog
      v-model:visible="statusActionVisible"
      :action="currentStatusAction"
      @submit="handleStatusActionSubmit"
    />
  </div>
</template>

<style scoped lang="scss">
.personal-item-row-actions {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
}

.personal-item-row-actions :deep(.el-button + .el-button) {
  margin-left: 0;
}

:deep(.personal-item-row-action-btn) {
  padding: 3px;
  min-width: auto;
  height: auto;
  line-height: 1;
}

:deep(.personal-item-title-link) {
  max-width: 100%;
  padding: 0;
  vertical-align: baseline;
}

:deep(.personal-item-title-link > span) {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
