<script setup lang="tsx">
import { computed, markRaw, reactive, ref, watch } from 'vue';
import { ElButton, ElTag } from 'element-plus';
import dayjs from 'dayjs';
import {
  fetchExportOvertimeApplications,
  fetchGetMySubordinateTree,
  fetchGetOvertimeApplicationPage,
  fetchGetTeamOvertimeSummary
} from '@/service/api';
import { useUIPaginatedTable } from '@/hooks/common/table';
import { useAuth } from '@/hooks/business/auth';
import SubordinateSelector from '@/components/custom/subordinate-selector.vue';
import TeamContextPanel from '@/components/custom/team-context-panel.vue';
import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell';
import {
  type TeamViewContext,
  type TeamViewMode,
  collectSubordinateUserIds,
  findSubordinateNode
} from '../shared/team-dashboard';
import OvertimeApplicationApprovalRecordDialog from './modules/overtime-application-approval-record-dialog.vue';
import OvertimeApplicationDetailDialog from './modules/overtime-application-detail-dialog.vue';
import OvertimeApplicationOperateDialog from './modules/overtime-application-operate-dialog.vue';
import OvertimeApplicationSearch from './modules/overtime-application-search.vue';
import {
  downloadBlob,
  formatEmptyText,
  formatOvertimeDate,
  formatOvertimeDateTime,
  getOvertimeApplicationStatusLabel,
  resolveOvertimeApplicationStatusTagType
} from './modules/overtime-application-shared';
import IconMdiEyeOutline from '~icons/mdi/eye-outline';
import IconMdiFileDocumentCheckOutline from '~icons/mdi/file-document-check-outline';
import IconMdiPencilOutline from '~icons/mdi/pencil-outline';
import IconMdiDownloadOutline from '~icons/mdi/download-outline';

defineOptions({ name: 'OvertimeApplication' });

type OvertimeApplicationPageResponse = Awaited<ReturnType<typeof fetchGetOvertimeApplicationPage>>;

function getInitSearchParams(): Api.OvertimeApplication.OvertimeApplicationSearchParams {
  return {
    pageNo: 1,
    pageSize: 10,
    keyword: undefined,
    applicantIds: undefined,
    applicantName: undefined,
    approverId: undefined,
    approverName: undefined,
    statusCode: undefined,
    overtimeDate: undefined,
    createTime: undefined
  };
}

function transformPageResult(response: OvertimeApplicationPageResponse, pageNo: number, pageSize: number) {
  if (!response.error && response.data) {
    return {
      data: response.data.list,
      pageNum: pageNo,
      pageSize,
      total: response.data.total
    };
  }

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

const searchParams = reactive(getInitSearchParams());
const { hasAuth } = useAuth();
const teamViewMode = ref<TeamViewMode>('self');
const subordinateTreeLoading = ref(false);
const subordinateTree = ref<Api.SystemManage.MySubordinateTreeNode | null>(null);
const selectedSubordinateUserId = ref<string | null>(null);
const teamSummaryLoading = ref(false);
const teamSummary = ref<Api.OvertimeApplication.TeamOvertimeSummary | null>(null);
const operateVisible = ref(false);
const detailVisible = ref(false);
const approvalRecordVisible = ref(false);
const operateType = ref<'add' | 'edit'>('add');
const currentRow = ref<Api.OvertimeApplication.OvertimeApplication | null>(null);
const exporting = ref(false);

const ACTION_ICON_MAP = {
  detail: markRaw(IconMdiEyeOutline),
  approvalRecord: markRaw(IconMdiFileDocumentCheckOutline),
  edit: markRaw(IconMdiPencilOutline)
};

const canUseTeamDashboard = computed(() => hasAuth('project:overtime-application:team-dashboard'));
const allSubordinateUserIds = computed(() => collectSubordinateUserIds(subordinateTree.value));
const subordinateOptions = computed(() => {
  const options: Array<{ label: string; value: string }> = [];

  const walk = (nodes?: Api.SystemManage.MySubordinateTreeNode[] | null) => {
    nodes?.forEach(node => {
      options.push({ label: node.userNickname, value: node.userId });
      walk(node.children ?? null);
    });
  };

  walk(subordinateTree.value?.children ?? null);
  return options;
});
const selectedSubordinateNode = computed(() =>
  findSubordinateNode(subordinateTree.value, selectedSubordinateUserId.value)
);
const isTeamMode = computed(() => teamViewMode.value === 'team');
const isRootSelected = computed(() => Boolean(isTeamMode.value && selectedSubordinateNode.value?.isRoot));
const summaryPeriodLabel = computed(() => {
  if (teamSummary.value?.overtimeDateStart && teamSummary.value?.overtimeDateEnd) {
    return `${teamSummary.value.overtimeDateStart} 至 ${teamSummary.value.overtimeDateEnd}`;
  }
  return '';
});
const selectedTeamLabel = computed(() => {
  if (!isTeamMode.value) return '我自己';
  if (!selectedSubordinateNode.value) return '--';
  return selectedSubordinateNode.value.isRoot ? '全部下属' : selectedSubordinateNode.value.userNickname;
});
const teamContext = computed<TeamViewContext | null>(() => {
  if (!canUseTeamDashboard.value) return null;

  return {
    mode: teamViewMode.value,
    selectedUserId: selectedSubordinateUserId.value,
    selectedUserIds:
      isTeamMode.value && selectedSubordinateUserId.value && !isRootSelected.value
        ? [selectedSubordinateUserId.value]
        : [],
    isRootSelected: isRootSelected.value,
    allSubordinateUserIds: allSubordinateUserIds.value,
    selectedLabel: selectedTeamLabel.value
  };
});
const currentApplicantIds = computed(() => {
  if (!isTeamMode.value) return null;
  if (isRootSelected.value) return [];
  return teamContext.value?.selectedUserIds ?? [];
});
const resolvedApplicantIds = computed(() => {
  if (!isTeamMode.value) {
    return undefined;
  }

  if (searchParams.applicantIds?.length) {
    return searchParams.applicantIds;
  }

  return currentApplicantIds.value;
});

const { columns, columnChecks, data, loading, getDataByPage, mobilePagination, reloadColumns } = useUIPaginatedTable<
  OvertimeApplicationPageResponse,
  Api.OvertimeApplication.OvertimeApplication
>({
  paginationProps: {
    currentPage: searchParams.pageNo,
    pageSize: searchParams.pageSize
  },
  api: () =>
    fetchGetOvertimeApplicationPage({
      ...searchParams,
      applicantIds: resolvedApplicantIds.value
    }),
  transform: response => transformPageResult(response, searchParams.pageNo ?? 1, searchParams.pageSize ?? 10),
  onPaginationParamsChange: params => {
    searchParams.pageNo = params.currentPage ?? 1;
    searchParams.pageSize = params.pageSize ?? 10;
  },
  columns: () => {
    const cols: UI.TableColumn<Api.OvertimeApplication.OvertimeApplication>[] = [
      { prop: 'index', type: 'index', label: '序号', width: 64 },
      {
        prop: 'overtimeDate',
        label: '学习日期',
        width: 120,
        formatter: row => formatOvertimeDate(row.overtimeDate)
      }
    ];

    if (isTeamMode.value) {
      cols.push({ prop: 'applicantName', label: '申请人', minWidth: 120, showOverflowTooltip: true });
    }

    cols.push(
      { prop: 'overtimeDuration', label: '学习时长', width: 110, showOverflowTooltip: true },
      {
        prop: 'overtimeReason',
        label: '学习原因',
        minWidth: 180,
        className: 'overtime-application__cell-ellipsis',
        formatter: row => formatEmptyText(row.overtimeReason)
      },
      {
        prop: 'overtimeContent',
        label: '学习内容',
        minWidth: 200,
        className: 'overtime-application__cell-ellipsis',
        formatter: row => formatEmptyText(row.overtimeContent)
      },
      {
        prop: 'statusCode',
        label: '状态',
        width: 110,
        align: 'center',
        formatter: row => (
          <ElTag type={resolveOvertimeApplicationStatusTagType(row.statusCode)}>
            {getOvertimeApplicationStatusLabel(row.statusCode, row.statusName)}
          </ElTag>
        )
      },
      { prop: 'approverName', label: '审批人', minWidth: 80, showOverflowTooltip: true },
      {
        prop: 'submitTime',
        label: '提交时间',
        minWidth: 150,
        formatter: row => formatOvertimeDateTime(row.submitTime)
      },
      {
        prop: 'approvalTime',
        label: '审批时间',
        minWidth: 150,
        formatter: row => formatOvertimeDateTime(row.approvalTime)
      },
      {
        prop: 'operate',
        label: '操作',
        width: isTeamMode.value ? 140 : 170,
        align: 'center',
        fixed: 'right',
        formatter: row => <BusinessTableActionCell actions={getRowActions(row)} variant="icon" />
      }
    );

    return cols;
  }
});

const totalCount = computed(() => mobilePagination.value.total || data.value.length);

function getRowActions(row: Api.OvertimeApplication.OvertimeApplication): BusinessTableAction[] {
  const actions: BusinessTableAction[] = [
    {
      key: 'detail',
      label: '查看',
      buttonType: 'primary',
      icon: ACTION_ICON_MAP.detail,
      onClick: () => openDetail(row)
    }
  ];

  if (isTeamMode.value) {
    if (['approved', 'rejected'].includes(row.statusCode)) {
      actions.push({
        key: 'approval-record',
        label: '审批记录',
        buttonType: 'info',
        icon: ACTION_ICON_MAP.approvalRecord,
        onClick: () => openApprovalRecord(row)
      });
    }

    return actions;
  }

  if (row.statusCode === 'rejected' && row.allowEdit) {
    actions.push({
      key: 'edit',
      label: '修改',
      buttonType: 'primary',
      icon: ACTION_ICON_MAP.edit,
      onClick: () => openEdit(row)
    });
  }

  if (['approved', 'rejected'].includes(row.statusCode)) {
    actions.push({
      key: 'approval-record',
      label: '审批记录',
      buttonType: 'info',
      icon: ACTION_ICON_MAP.approvalRecord,
      onClick: () => openApprovalRecord(row)
    });
  }

  return actions;
}

function openAdd() {
  operateType.value = 'add';
  currentRow.value = null;
  operateVisible.value = true;
}

function openEdit(row: Api.OvertimeApplication.OvertimeApplication) {
  operateType.value = 'edit';
  currentRow.value = row;
  operateVisible.value = true;
}

function openDetail(row: Api.OvertimeApplication.OvertimeApplication) {
  currentRow.value = row;
  detailVisible.value = true;
}

function openApprovalRecord(row: Api.OvertimeApplication.OvertimeApplication) {
  currentRow.value = row;
  approvalRecordVisible.value = true;
}

async function reloadTable(page = searchParams.pageNo ?? 1) {
  await getDataByPage(page);
}

function resetSearchParams() {
  const pageSize = searchParams.pageSize ?? 10;
  Object.assign(searchParams, getInitSearchParams(), { pageSize });
  reloadTable(1);
  loadTeamSummary();
}

function handleSearch() {
  reloadTable(1);
  loadTeamSummary();
}

function handleSubmitted() {
  operateVisible.value = false;
  reloadTable(searchParams.pageNo ?? 1);
}

function createExportParams() {
  const { pageNo: _pageNo, pageSize: _pageSize, ...params } = searchParams;
  return {
    ...params,
    applicantIds: resolvedApplicantIds.value
  };
}

async function handleExport() {
  exporting.value = true;
  const { error, data: blob } = await fetchExportOvertimeApplications({
    ...createExportParams(),
    applicantIds: resolvedApplicantIds.value
  });
  exporting.value = false;

  if (error || !blob) {
    return;
  }

  downloadBlob(blob, `业务学习_${dayjs().format('YYYY-MM-DD')}.xls`);
}

async function loadSubordinateTree() {
  if (!canUseTeamDashboard.value) return;

  subordinateTreeLoading.value = true;
  const { error, data: treeData } = await fetchGetMySubordinateTree();
  subordinateTreeLoading.value = false;

  subordinateTree.value = error || !treeData ? null : treeData;
  selectedSubordinateUserId.value = treeData?.userId || null;
}

async function loadTeamSummary() {
  if (!isRootSelected.value) {
    teamSummary.value = null;
    return;
  }

  const summaryParams: Api.OvertimeApplication.TeamOvertimeSummaryParams = {};
  const dateRange = searchParams.overtimeDate;

  if (dateRange?.length === 2) {
    summaryParams.overtimeDateStart = dateRange[0];
    summaryParams.overtimeDateEnd = dateRange[1];
  }

  teamSummaryLoading.value = true;
  const { error, data: summaryData } = await fetchGetTeamOvertimeSummary(summaryParams);
  teamSummaryLoading.value = false;

  teamSummary.value = error || !summaryData ? null : summaryData;
}

async function handleTeamViewModeChange(mode: TeamViewMode) {
  teamViewMode.value = mode;

  if (mode === 'team') {
    if (!subordinateTree.value) {
      await loadSubordinateTree();
    }
    if (!selectedSubordinateUserId.value) {
      selectedSubordinateUserId.value = subordinateTree.value?.userId || null;
    }
  }

  await reloadTable(1);
}

watch(
  () => [teamViewMode.value, selectedSubordinateUserId.value],
  async () => {
    if (!isTeamMode.value) return;
    await reloadTable(1);
  }
);

watch(
  () => isTeamMode.value,
  () => {
    reloadColumns();
  }
);

watch(
  () => isRootSelected.value,
  () => {
    loadTeamSummary();
  }
);
</script>

<template>
  <div class="overtime-application-page">
    <TeamContextPanel
      v-if="canUseTeamDashboard"
      v-model:mode="teamViewMode"
      :loading="subordinateTreeLoading"
      :selected-label="selectedTeamLabel"
      :subordinate-count="subordinateTree?.subordinateCount || 0"
      @update:mode="handleTeamViewModeChange"
    >
      <div v-if="isRootSelected" v-loading="teamSummaryLoading" class="team-overtime-summary">
        <div v-if="summaryPeriodLabel" class="team-overtime-summary__period">{{ summaryPeriodLabel }}</div>
        <div class="team-overtime-summary__grid">
          <div class="team-overtime-summary__item">
            <span class="team-overtime-summary__label">申请单数</span>
            <strong class="team-overtime-summary__value">{{ teamSummary?.totalApplicationCount ?? 0 }}</strong>
          </div>
          <div class="team-overtime-summary__item">
            <span class="team-overtime-summary__label">待审批</span>
            <strong class="team-overtime-summary__value">{{ teamSummary?.pendingCount ?? 0 }}</strong>
          </div>
          <div class="team-overtime-summary__item">
            <span class="team-overtime-summary__label">已通过</span>
            <strong class="team-overtime-summary__value">{{ teamSummary?.approvedCount ?? 0 }}</strong>
          </div>
          <div class="team-overtime-summary__item">
            <span class="team-overtime-summary__label">已退回</span>
            <strong class="team-overtime-summary__value">{{ teamSummary?.rejectedCount ?? 0 }}</strong>
          </div>
        </div>
      </div>
    </TeamContextPanel>

    <div class="overtime-application-page__content" :class="{ 'overtime-application-page__content--team': isTeamMode }">
      <div v-if="canUseTeamDashboard && isTeamMode" class="overtime-application-page__sidebar">
        <SubordinateSelector
          v-model:selected-user-id="selectedSubordinateUserId"
          :loading="subordinateTreeLoading"
          :data="subordinateTree"
        />
      </div>

      <div class="overtime-application-page__main">
        <OvertimeApplicationSearch
          v-model:model="searchParams"
          :team-mode="isTeamMode"
          :subordinate-options="subordinateOptions"
          @reset="resetSearchParams"
          @search="handleSearch"
        />

        <ElCard class="flex-1-hidden card-wrapper" body-class="business-table-card-body">
          <template #header>
            <div class="flex flex-wrap items-center justify-between gap-12px">
              <div class="flex items-center gap-10px">
                <p class="text-16px font-600">业务学习</p>
                <ElTag effect="plain">{{ totalCount }}</ElTag>
              </div>
              <TableHeaderOperation v-model:columns="columnChecks" :loading="loading" @refresh="reloadTable">
                <template #default>
                  <ElButton plain :loading="exporting" @click="handleExport">
                    <template #icon>
                      <icon-mdi-download class="text-icon" />
                    </template>
                    导出
                  </ElButton>
                  <ElButton v-if="!isTeamMode" 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 v-loading="loading" height="100%" border row-key="id" :data="data">
              <template v-for="col in columns" :key="String(col.prop)">
                <ElTableColumn 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>
      </div>
    </div>

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

    <OvertimeApplicationDetailDialog v-model:visible="detailVisible" :row-data="currentRow" />

    <OvertimeApplicationApprovalRecordDialog v-model:visible="approvalRecordVisible" :row-data="currentRow" />
  </div>
</template>

<style scoped>
.overtime-application-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
  height: 100%;
  overflow: hidden;
}

.overtime-application-page__content {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
}

.overtime-application-page__main {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  min-height: 0;
}

@media (min-width: 1280px) {
  .overtime-application-page__content--team {
    display: grid;
    grid-template-columns: 240px minmax(0, 1fr);
  }

  .overtime-application-page__sidebar {
    min-height: 0;
  }
}

:deep(.overtime-application__reason-link) {
  max-width: 100%;
  padding: 0;
  vertical-align: baseline;
}

:deep(.overtime-application__reason-link > span) {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 学习原因/学习内容：单元格内容溢出时仅显示省略号，不弹出 tooltip */
:deep(.overtime-application__cell-ellipsis .cell) {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.team-overtime-summary {
  display: grid;
  gap: 12px;
}

.team-overtime-summary__period {
  color: var(--el-text-color-secondary);
  font-size: 12px;
}

.team-overtime-summary__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}

.team-overtime-summary__item {
  display: grid;
  gap: 8px;
  padding: 14px 16px;
  border: 1px solid var(--el-border-color-light);
  border-radius: 8px;
  background: var(--el-fill-color-blank);
}

.team-overtime-summary__label {
  color: var(--el-text-color-secondary);
  font-size: 12px;
}

.team-overtime-summary__value {
  color: var(--el-text-color-primary);
  font-size: 22px;
  font-weight: 600;
  line-height: 1.2;
}
</style>
