<script setup lang="tsx">
import { computed, markRaw, reactive, ref } from 'vue';
import { ElButton, ElMessageBox, ElTag } from 'element-plus';
import dayjs from 'dayjs';
import {
  fetchCancelOvertimeApplication,
  fetchDeleteOvertimeApplication,
  fetchExportOvertimeApplications,
  fetchGetOvertimeApplicationPage
} from '@/service/api';
import { useUIPaginatedTable } from '@/hooks/common/table';
import BusinessTableActionCell, { type BusinessTableAction } from '@/components/custom/business-table-action-cell';
import OvertimeApplicationActionDialog from './modules/overtime-application-action-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 OvertimeApplicationStatusLogDialog from './modules/overtime-application-status-log-dialog.vue';
import {
  downloadBlob,
  formatEmptyText,
  formatOvertimeDate,
  formatOvertimeDateTime,
  getOvertimeApplicationStatusLabel,
  resolveOvertimeApplicationStatusTagType
} from './modules/overtime-application-shared';
import IconMdiCloseCircleOutline from '~icons/mdi/close-circle-outline';
import IconMdiDeleteOutline from '~icons/mdi/delete-outline';
import IconMdiEyeOutline from '~icons/mdi/eye-outline';
import IconMdiHistory from '~icons/mdi/history';
import IconMdiPencilOutline from '~icons/mdi/pencil-outline';

defineOptions({ name: 'OvertimeApplication' });

type OvertimeApplicationPageResponse = Awaited<ReturnType<typeof fetchGetOvertimeApplicationPage>>;
type ActionType = 'cancel';

function getInitSearchParams(): Api.OvertimeApplication.OvertimeApplicationSearchParams {
  return {
    pageNo: 1,
    pageSize: 10,
    keyword: 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 operateVisible = ref(false);
const detailVisible = ref(false);
const statusLogVisible = ref(false);
const actionVisible = ref(false);
const operateType = ref<'add' | 'edit'>('add');
const currentRow = ref<Api.OvertimeApplication.OvertimeApplication | null>(null);
const currentActionType = ref<ActionType>('cancel');
const actionSubmitting = ref(false);
const exporting = ref(false);

const ACTION_ICON_MAP = {
  detail: markRaw(IconMdiEyeOutline),
  statusLog: markRaw(IconMdiHistory),
  edit: markRaw(IconMdiPencilOutline),
  cancel: markRaw(IconMdiCloseCircleOutline),
  delete: markRaw(IconMdiDeleteOutline)
};

const { columns, columnChecks, data, loading, getDataByPage, mobilePagination } = useUIPaginatedTable<
  OvertimeApplicationPageResponse,
  Api.OvertimeApplication.OvertimeApplication
>({
  paginationProps: {
    currentPage: searchParams.pageNo,
    pageSize: searchParams.pageSize
  },
  api: () => fetchGetOvertimeApplicationPage(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: 'index', type: 'index', label: '序号', width: 64 },
    { prop: 'applicantName', label: '申请人', minWidth: 120, showOverflowTooltip: true },
    {
      prop: 'overtimeDate',
      label: '加班日期',
      width: 120,
      formatter: row => formatOvertimeDate(row.overtimeDate)
    },
    { prop: 'overtimeDuration', label: '加班时长', width: 110, showOverflowTooltip: true },
    {
      prop: 'overtimeReason',
      label: '加班原因',
      minWidth: 180,
      showOverflowTooltip: true,
      formatter: row => formatEmptyText(row.overtimeReason)
    },
    {
      prop: 'overtimeContent',
      label: '加班内容',
      minWidth: 200,
      showOverflowTooltip: true,
      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: 120, showOverflowTooltip: true },
    {
      prop: 'submitTime',
      label: '提交时间',
      minWidth: 170,
      formatter: row => formatOvertimeDateTime(row.submitTime)
    },
    {
      prop: 'approvalTime',
      label: '审核时间',
      minWidth: 170,
      formatter: row => formatOvertimeDateTime(row.approvalTime)
    },
    {
      prop: 'operate',
      label: '操作',
      width: 170,
      align: 'center',
      fixed: 'right',
      formatter: row => <BusinessTableActionCell actions={getRowActions(row)} variant="icon" />
    }
  ]
});

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 (row.statusCode === 'rejected' && row.allowEdit) {
    actions.push({
      key: 'edit',
      label: '修改',
      buttonType: 'primary',
      icon: ACTION_ICON_MAP.edit,
      onClick: () => openEdit(row)
    });
  }

  actions.push({
    key: 'status-log',
    label: '状态日志',
    buttonType: 'info',
    icon: ACTION_ICON_MAP.statusLog,
    onClick: () => openStatusLog(row)
  });

  if (row.statusCode === 'pending' || row.statusCode === 'rejected') {
    actions.push({
      key: 'cancel',
      label: '撤销',
      buttonType: 'danger',
      icon: ACTION_ICON_MAP.cancel,
      onClick: () => openCancel(row)
    });
  }

  if (row.statusCode === 'cancelled') {
    actions.push({
      key: 'delete',
      label: '删除',
      buttonType: 'danger',
      icon: ACTION_ICON_MAP.delete,
      onClick: () => handleDelete(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 openStatusLog(row: Api.OvertimeApplication.OvertimeApplication) {
  currentRow.value = row;
  statusLogVisible.value = true;
}

function openCancel(row: Api.OvertimeApplication.OvertimeApplication) {
  currentRow.value = row;
  currentActionType.value = 'cancel';
  actionVisible.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);
}

function handleSearch() {
  reloadTable(1);
}

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

async function handleActionSubmit(reason: string | null) {
  if (!currentRow.value) {
    return;
  }

  actionSubmitting.value = true;
  const { error } = await fetchCancelOvertimeApplication(currentRow.value.id, { reason });
  actionSubmitting.value = false;

  if (error) {
    return;
  }

  actionVisible.value = false;
  window.$message?.success('加班申请已撤销');
  await reloadTable(searchParams.pageNo ?? 1);
}

async function handleDelete(row: Api.OvertimeApplication.OvertimeApplication) {
  try {
    await ElMessageBox.confirm(
      `确定删除 ${row.applicantName} ${formatOvertimeDate(row.overtimeDate)} 的加班申请吗？`,
      '删除确认',
      {
        type: 'warning',
        confirmButtonText: '确定',
        cancelButtonText: '取消'
      }
    );
  } catch {
    return;
  }

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

  if (error) {
    return;
  }

  window.$message?.success('加班申请已删除');
  await reloadTable(searchParams.pageNo ?? 1);
}

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

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

  downloadBlob(blob, `加班申请_${dayjs().format('YYYY-MM-DD')}.xls`);
}
</script>

<template>
  <div class="flex-col-stretch gap-16px overflow-hidden">
    <OvertimeApplicationSearch 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 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 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>

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

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

    <OvertimeApplicationStatusLogDialog v-model:visible="statusLogVisible" :row-data="currentRow" />

    <OvertimeApplicationActionDialog
      v-model:visible="actionVisible"
      :action-type="currentActionType"
      :loading="actionSubmitting"
      @submit="handleActionSubmit"
    />
  </div>
</template>

<style scoped>
: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;
}
</style>
