<script setup lang="ts">
import type { FormInstance } from 'element-plus';

import { computed, nextTick, onMounted, ref } from 'vue';

import { Plus } from '@element-plus/icons-vue';
import {
  ElButton,
  ElForm,
  ElFormItem,
  ElIcon,
  ElInput,
  ElMessage,
  ElMessageBox,
  ElTable,
  ElTableColumn,
} from 'element-plus';

import { api } from '#/api/request';
import DictSelect from '#/components/dict/DictSelect.vue';
import { $t } from '#/locales';
import { useDictStore } from '#/store';

import SysDeptModal from './SysDeptModal.vue';

interface DepartmentTreeNode {
  children?: DepartmentTreeNode[];
  id: number | string;
  [key: string]: any;
}

onMounted(() => {
  void getTree();
  initDict();
});

const formRef = ref<FormInstance>();
const tableRef = ref<InstanceType<typeof ElTable>>();
const treeData = ref<DepartmentTreeNode[]>([]);
const selectedRows = ref<any[]>([]);
const loading = ref(false);
const batchActionType = ref<'' | 'delete' | 'disable' | 'enable'>('');
const rowDeleteLoadingId = ref('');
const rowStatusLoadingId = ref('');
const saveDialog = ref();
const formInline = ref({
  deptName: '',
  status: '',
});
const dictStore = useDictStore();
const selectedCount = computed(() => selectedRows.value.length);
const batchActionLoading = computed(() => batchActionType.value !== '');
const rowActionLoading = computed(
  () => rowDeleteLoadingId.value !== '' || rowStatusLoadingId.value !== '',
);

function initDict() {
  dictStore.fetchDictionary('dataStatus');
}
function search(formEl: FormInstance | undefined) {
  formEl?.validate((valid) => {
    if (valid) {
      void getTree();
    }
  });
}
function reset(formEl: FormInstance | undefined) {
  formEl?.resetFields();
  void getTree();
}
function showDialog(row: any) {
  saveDialog.value.openDialog({ ...row });
}

function isRootDept(row: any) {
  return row?.deptCode === 'root_dept';
}

function isSelectable(row: any) {
  return !isRootDept(row);
}

function handleSelectionChange(rows: any[]) {
  selectedRows.value = rows;
}

function clearSelection() {
  selectedRows.value = [];
  tableRef.value?.clearSelection();
}

function setTreeExpanded(rows: DepartmentTreeNode[], expanded: boolean) {
  for (const row of rows) {
    if (!row.children?.length) {
      continue;
    }
    tableRef.value?.toggleRowExpansion(row, expanded);
    setTreeExpanded(row.children, expanded);
  }
}

function getSelectedIds() {
  return selectedRows.value
    .map((row) => row?.id)
    .filter((id) => id !== null && id !== undefined);
}

function getErrorMessage(error: any) {
  return error?.message || $t('sysDept.operationFailed');
}

async function confirmAction(message: string) {
  try {
    await ElMessageBox.confirm(message, $t('message.noticeTitle'), {
      confirmButtonText: $t('message.ok'),
      cancelButtonText: $t('message.cancel'),
      type: 'warning',
    });
    return true;
  } catch {
    return false;
  }
}

async function changeStatus(row: any) {
  const status = row.status === 1 ? 0 : 1;
  const action = status === 1 ? $t('sysDept.enable') : $t('sysDept.disable');
  const confirmed = await confirmAction(
    $t('sysDept.statusConfirm', {
      action,
      name: row.deptName,
    }),
  );
  if (!confirmed) {
    return;
  }

  rowStatusLoadingId.value = String(row.id);
  try {
    const res = await api.post('/api/v1/sysDept/changeStatusBatch', {
      ids: [row.id],
      status,
    });
    if (res.errorCode !== 0) {
      ElMessage.error(res.message || $t('sysDept.operationFailed'));
      return;
    }
    ElMessage.success($t('sysDept.operationSuccess'));
    await getTree();
  } catch (error: any) {
    ElMessage.error(getErrorMessage(error));
  } finally {
    rowStatusLoadingId.value = '';
  }
}

async function changeSelectedStatus(status: 0 | 1) {
  const ids = getSelectedIds();
  if (ids.length === 0) {
    ElMessage.warning($t('sysDept.selectRequired'));
    return;
  }
  const action = status === 1 ? $t('sysDept.enable') : $t('sysDept.disable');
  const confirmed = await confirmAction(
    $t('sysDept.batchStatusConfirm', {
      action,
      count: ids.length,
    }),
  );
  if (!confirmed) {
    return;
  }

  batchActionType.value = status === 1 ? 'enable' : 'disable';
  try {
    const res = await api.post('/api/v1/sysDept/changeStatusBatch', {
      ids,
      status,
    });
    if (res.errorCode !== 0) {
      ElMessage.error(res.message || $t('sysDept.operationFailed'));
      return;
    }
    ElMessage.success($t('sysDept.operationSuccess'));
    await getTree();
  } catch (error: any) {
    ElMessage.error(getErrorMessage(error));
  } finally {
    batchActionType.value = '';
  }
}

async function submitDelete(
  url: string,
  payload: Record<string, any>,
  confirmMessage: string,
  isBatch: boolean,
) {
  const confirmed = await confirmAction(confirmMessage);
  if (!confirmed) {
    return;
  }

  if (isBatch) {
    batchActionType.value = 'delete';
  } else {
    rowDeleteLoadingId.value = String(payload.id);
  }
  try {
    const res = await api.post(url, payload);
    if (res.errorCode !== 0) {
      ElMessage.error(res.message || $t('sysDept.operationFailed'));
      return;
    }
    ElMessage.success($t('sysDept.operationSuccess'));
    await getTree();
  } catch (error: any) {
    ElMessage.error(getErrorMessage(error));
  } finally {
    if (isBatch) {
      batchActionType.value = '';
    } else {
      rowDeleteLoadingId.value = '';
    }
  }
}

function remove(row: any) {
  void submitDelete(
    '/api/v1/sysDept/remove',
    { id: row.id },
    $t('sysDept.deleteConfirm', { name: row.deptName }),
    false,
  );
}

function removeSelected() {
  const ids = getSelectedIds();
  if (ids.length === 0) {
    ElMessage.warning($t('sysDept.selectRequired'));
    return;
  }
  void submitDelete(
    '/api/v1/sysDept/removeBatch',
    { ids },
    $t('sysDept.batchDeleteConfirm', { count: ids.length }),
    true,
  );
}

async function getTree() {
  loading.value = true;
  try {
    const keyword = formInline.value.deptName.trim();
    const res = await api.get('/api/v1/sysDept/list', {
      params: {
        asTree: true,
        keyword: keyword || undefined,
        status: formInline.value.status || undefined,
      },
    });
    treeData.value = res.data || [];
    clearSelection();
    await nextTick();
    setTreeExpanded(treeData.value, keyword.length > 0);
  } finally {
    loading.value = false;
  }
}
</script>

<template>
  <div class="flex h-full flex-col gap-1.5 p-6">
    <SysDeptModal ref="saveDialog" @reload="reset" />
    <div class="flex flex-wrap items-start justify-between gap-3">
      <ElForm ref="formRef" :inline="true" :model="formInline" class="min-w-0">
        <ElFormItem prop="deptName" class="!mr-3">
          <ElInput
            class="search-input"
            v-model="formInline.deptName"
            clearable
            :placeholder="$t('sysDept.searchPlaceholder')"
            @clear="search(formRef)"
            @keyup.enter="search(formRef)"
          />
        </ElFormItem>
        <ElFormItem prop="status" class="!mr-3">
          <DictSelect
            class="status-select"
            v-model="formInline.status"
            dict-code="dataStatus"
            :placeholder="$t('sysDept.status')"
          />
        </ElFormItem>
        <ElFormItem>
          <ElButton @click="search(formRef)" type="primary">
            {{ $t('button.query') }}
          </ElButton>
          <ElButton @click="reset(formRef)">
            {{ $t('button.reset') }}
          </ElButton>
        </ElFormItem>
      </ElForm>
      <div class="department-actions">
        <div v-if="selectedCount > 0" class="department-batch-actions">
          <ElButton
            v-access:code="'/api/v1/sysDept/save'"
            class="department-batch-action is-enable"
            :disabled="batchActionLoading || rowActionLoading"
            :loading="batchActionType === 'enable'"
            text
            @click="changeSelectedStatus(1)"
          >
            {{ $t('sysDept.batchEnable') }}
          </ElButton>
          <ElButton
            v-access:code="'/api/v1/sysDept/save'"
            class="department-batch-action is-disable"
            :disabled="batchActionLoading || rowActionLoading"
            :loading="batchActionType === 'disable'"
            text
            @click="changeSelectedStatus(0)"
          >
            {{ $t('sysDept.batchDisable') }}
          </ElButton>
          <ElButton
            v-access:code="'/api/v1/sysDept/remove'"
            class="department-batch-action is-danger"
            :disabled="batchActionLoading || rowActionLoading"
            :loading="batchActionType === 'delete'"
            text
            @click="removeSelected"
          >
            {{ $t('sysDept.batchDelete') }}
          </ElButton>
        </div>
        <ElButton
          v-access:code="'/api/v1/sysDept/save'"
          class="department-add-action"
          @click="showDialog({})"
          type="primary"
        >
          <ElIcon class="mr-1">
            <Plus />
          </ElIcon>
          {{ $t('button.add') }}
        </ElButton>
      </div>
    </div>

    <div class="bg-background border-border flex-1 rounded-lg border p-5">
      <ElTable
        ref="tableRef"
        :data="treeData"
        :tree-props="{ children: 'children', checkStrictly: true }"
        row-key="id"
        v-loading="loading"
        border
        @selection-change="handleSelectionChange"
      >
        <ElTableColumn type="selection" width="48" :selectable="isSelectable" />
        <ElTableColumn prop="deptName" :label="$t('sysDept.deptName')">
          <template #default="{ row }">
            {{ row.deptName }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="deptCode" :label="$t('sysDept.deptCode')">
          <template #default="{ row }">
            {{ row.deptCode }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="sortNo" :label="$t('sysDept.sortNo')">
          <template #default="{ row }">
            {{ row.sortNo }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="status" :label="$t('sysDept.status')" width="120">
          <template #default="{ row }">
            {{ dictStore.getDictLabel('dataStatus', row.status) }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="created" :label="$t('sysDept.created')">
          <template #default="{ row }">
            {{ row.created }}
          </template>
        </ElTableColumn>
        <ElTableColumn prop="remark" :label="$t('sysDept.remark')">
          <template #default="{ row }">
            {{ row.remark }}
          </template>
        </ElTableColumn>
        <ElTableColumn
          :label="$t('common.handle')"
          width="224"
          align="right"
          fixed="right"
        >
          <template #default="{ row }">
            <div class="department-row-actions">
              <ElButton
                v-access:code="'/api/v1/sysDept/save'"
                :disabled="
                  batchActionLoading ||
                  rowDeleteLoadingId !== '' ||
                  rowStatusLoadingId !== '' ||
                  (isRootDept(row) && row.status === 1)
                "
                :loading="rowStatusLoadingId === String(row.id)"
                link
                type="primary"
                @click="changeStatus(row)"
              >
                {{
                  row.status === 1
                    ? $t('sysDept.disable')
                    : $t('sysDept.enable')
                }}
              </ElButton>
              <ElButton
                v-access:code="'/api/v1/sysDept/save'"
                :disabled="
                  batchActionLoading ||
                  rowDeleteLoadingId !== '' ||
                  rowStatusLoadingId !== ''
                "
                link
                type="primary"
                @click="showDialog(row)"
              >
                {{ $t('button.edit') }}
              </ElButton>
              <ElButton
                v-access:code="'/api/v1/sysDept/remove'"
                :disabled="
                  batchActionLoading ||
                  rowStatusLoadingId !== '' ||
                  rowDeleteLoadingId !== '' ||
                  isRootDept(row)
                "
                :loading="rowDeleteLoadingId === String(row.id)"
                link
                type="danger"
                @click="remove(row)"
              >
                {{ $t('button.delete') }}
              </ElButton>
            </div>
          </template>
        </ElTableColumn>
      </ElTable>
    </div>
  </div>
</template>

<style scoped>
.search-input {
  width: 300px;
  border-radius: 4px;
}

.status-select {
  width: 160px;
}

.department-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  justify-content: flex-end;
  min-height: 32px;
}

.department-batch-actions {
  box-sizing: border-box;
  display: inline-flex;
  gap: 2px;
  align-items: center;
  height: 32px;
  padding: 0 3px;
  background: hsl(var(--toolbar-bg));
  border: 1px solid hsl(var(--toolbar-border));
  border-radius: var(--radius-toolbar);
}

.department-actions :deep(.department-add-action) {
  height: 32px;
}

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

.department-batch-actions :deep(.department-batch-action) {
  height: 30px;
  min-height: 30px;
  padding: 0 12px;
  font-weight: 500;
  background: transparent;
  border-radius: var(--radius-control);
  transition:
    color var(--motion-duration-base) var(--motion-ease-standard),
    background-color var(--motion-duration-base) var(--motion-ease-standard),
    box-shadow var(--motion-duration-base) var(--motion-ease-standard);
}

.department-batch-actions :deep(.department-batch-action.is-enable) {
  color: hsl(var(--primary));
}

.department-batch-actions :deep(.department-batch-action.is-disable) {
  color: hsl(var(--text-strong));
}

.department-batch-actions :deep(.department-batch-action.is-danger) {
  color: hsl(var(--destructive));
}

.department-batch-actions
  :deep(.department-batch-action.is-enable:not(.is-disabled):hover),
.department-batch-actions
  :deep(.department-batch-action.is-enable:not(.is-disabled):focus-visible) {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 8%);
}

.department-batch-actions
  :deep(.department-batch-action.is-disable:not(.is-disabled):hover),
.department-batch-actions
  :deep(.department-batch-action.is-disable:not(.is-disabled):focus-visible) {
  color: hsl(var(--text-strong));
  background: hsl(var(--surface-contrast-soft));
}

.department-batch-actions
  :deep(.department-batch-action.is-danger:not(.is-disabled):hover),
.department-batch-actions
  :deep(.department-batch-action.is-danger:not(.is-disabled):focus-visible) {
  color: hsl(var(--destructive));
  background: hsl(var(--destructive) / 8%);
}

.department-batch-actions
  :deep(.department-batch-action:not(.is-disabled):focus-visible) {
  outline: none;
  box-shadow: 0 0 0 2px hsl(var(--primary) / 20%);
}

.department-row-actions {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: flex-end;
  white-space: nowrap;
}

@media (max-width: 960px) {
  .department-actions {
    width: 100%;
  }
}

@media (max-width: 640px) {
  .department-actions {
    justify-content: stretch;
  }

  .department-batch-actions {
    flex: 1;
  }

  .department-batch-actions :deep(.department-batch-action) {
    flex: 1;
    padding: 0 8px;
  }
}
</style>
