<script setup lang="tsx">
import { computed, onMounted, reactive, ref, watch } from 'vue';
import { ElButton, ElPopconfirm, ElTag } from 'element-plus';
import dayjs from 'dayjs';
import type { FlatResponseData } from '@sa/axios';
import { dictStatusRecord } from '@/constants/business';
import {
  fetchBatchDeleteDictData,
  fetchDeleteDictData,
  fetchDeleteDictType,
  fetchGetDictDataPage,
  fetchGetDictTypePage
} from '@/service/api';
import { useTableOperate, useUIPaginatedTable } from '@/hooks/common/table';
import { $t } from '@/locales';
import DictDataOperateModal from './modules/dict-data-operate-modal.vue';
import DictDataSearch from './modules/dict-data-search.vue';
import DictTypeOperateModal from './modules/dict-type-operate-modal.vue';
import DictTypeSearch from './modules/dict-type-search.vue';

defineOptions({ name: 'DictManage' });

function getInitDictTypeSearchParams(): Api.Dict.DictTypeSearchParams {
  return {
    pageNo: 1,
    pageSize: 200,
    name: undefined,
    type: undefined,
    status: undefined
  };
}

function getInitDictDataSearchParams(): Api.Dict.DictDataSearchParams {
  return {
    pageNo: 1,
    pageSize: 10,
    label: undefined,
    dictType: undefined,
    status: undefined
  };
}

function createEmptyDictPageResult<ApiData>(): Promise<FlatResponseData<any, Api.Dict.PageResult<ApiData>>> {
  return Promise.resolve({
    data: {
      list: [],
      total: 0
    },
    error: null,
    response: undefined
  } as unknown as FlatResponseData<any, Api.Dict.PageResult<ApiData>>);
}

function transformDictPage<ApiData>(
  response: FlatResponseData<any, Api.Dict.PageResult<ApiData>>,
  pageNo: number,
  pageSize: number
) {
  // 项目通用表格 hook 默认消费 data/pageNum/pageSize/total，
  // 这里把后端字典接口的 list/total 结构适配过去。
  const { data, error } = response;

  if (!error) {
    return {
      data: data.list,
      pageNum: pageNo,
      pageSize,
      total: data.total
    };
  }

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

function formatTime(value?: number | null) {
  if (!value) {
    return '--';
  }

  return dayjs(value).format('YYYY-MM-DD HH:mm:ss');
}

function getDictStatusLabel(status: Api.Dict.DictStatus) {
  return $t(dictStatusRecord[String(status) as '0' | '1']);
}

function getDictStatusTagType(status: Api.Dict.DictStatus): UI.ThemeColor {
  return status === 0 ? 'success' : 'info';
}

function formatColorType(value?: string | null) {
  return value?.trim() || '--';
}

const dictTypeSearchParams = reactive(getInitDictTypeSearchParams());
const dictDataSearchParams = reactive(getInitDictDataSearchParams());

const typeLoading = ref(false);
const typeList = ref<Api.Dict.DictType[]>([]);
const typeTotal = ref(0);
const currentTypeId = ref<number | null>(null);

const currentType = computed(() => typeList.value.find(item => item.id === currentTypeId.value) ?? null);
const currentTypeCode = computed(() => currentType.value?.type ?? '');

async function getDictTypeList() {
  typeLoading.value = true;

  const { error, data } = await fetchGetDictTypePage(dictTypeSearchParams);

  typeLoading.value = false;

  if (error) {
    typeList.value = [];
    typeTotal.value = 0;
    currentTypeId.value = null;
    return;
  }

  typeList.value = data.list;
  typeTotal.value = data.total;

  if (!data.list.length) {
    currentTypeId.value = null;
    return;
  }

  // 搜索后尽量保留原选中项；如果原项已不在结果中，则自动落到第一项。
  const matched = data.list.find(item => item.id === currentTypeId.value);

  if (!matched) {
    currentTypeId.value = data.list[0].id;
  }
}

const {
  drawerVisible: dictTypeModalVisible,
  operateType: dictTypeOperateType,
  editingData: editingDictTypeData,
  handleAdd: handleAddDictType,
  handleEdit: handleEditDictType,
  onDeleted: onDeletedDictType
} = useTableOperate<Api.Dict.DictType>(typeList, 'id', getDictTypeList);

const {
  columns: dictDataColumns,
  columnChecks: dictDataColumnChecks,
  data: dictData,
  loading: dictDataLoading,
  getData: getDictData,
  getDataByPage: getDictDataByPage,
  mobilePagination: dictDataPagination
} = useUIPaginatedTable<FlatResponseData<any, Api.Dict.PageResult<Api.Dict.DictData>>, Api.Dict.DictData>({
  paginationProps: {
    currentPage: dictDataSearchParams.pageNo,
    pageSize: dictDataSearchParams.pageSize
  },
  api: () => {
    if (!currentTypeCode.value) {
      // 左侧还没选中字典类型时，右侧直接返回空结果，避免发送无效请求。
      return createEmptyDictPageResult<Api.Dict.DictData>();
    }

    return fetchGetDictDataPage({
      ...dictDataSearchParams,
      dictType: currentTypeCode.value
    });
  },
  transform: response => transformDictPage(response, dictDataSearchParams.pageNo, dictDataSearchParams.pageSize),
  onPaginationParamsChange: params => {
    dictDataSearchParams.pageNo = params.currentPage ?? 1;
    dictDataSearchParams.pageSize = params.pageSize ?? 10;
  },
  columns: () => [
    { prop: 'selection', type: 'selection', width: 48 },
    { prop: 'index', type: 'index', label: $t('common.index'), width: 64 },
    { prop: 'label', label: $t('page.system.dict.dictLabel'), minWidth: 160 },
    { prop: 'value', label: $t('page.system.dict.dictValue'), minWidth: 180 },
    {
      prop: 'colorType',
      label: $t('page.system.dict.colorType'),
      width: 120,
      align: 'center',
      formatter: row => formatColorType(row.colorType)
    },
    { prop: 'sort', label: $t('page.system.dict.sort'), width: 90, align: 'center' },
    {
      prop: 'status',
      label: $t('page.system.dict.dictStatus'),
      width: 110,
      formatter: row => <ElTag type={getDictStatusTagType(row.status)}>{getDictStatusLabel(row.status)}</ElTag>
    },
    { prop: 'remark', label: $t('page.system.dict.remark'), minWidth: 180, showOverflowTooltip: true },
    {
      prop: 'createTime',
      label: $t('common.update'),
      minWidth: 170,
      formatter: row => formatTime(row.createTime)
    },
    {
      prop: 'operate',
      label: $t('common.operate'),
      width: 180,
      align: 'center',
      formatter: row => (
        <div class="flex-center">
          <ElButton type="primary" plain size="small" onClick={() => editDictData(row.id)}>
            {$t('common.edit')}
          </ElButton>
          <ElPopconfirm title={$t('common.confirmDelete')} onConfirm={() => handleDeleteSingleDictData(row.id)}>
            {{
              reference: () => (
                <ElButton type="danger" plain size="small">
                  {$t('common.delete')}
                </ElButton>
              )
            }}
          </ElPopconfirm>
        </div>
      )
    }
  ]
});

const {
  drawerVisible: dictDataModalVisible,
  operateType: dictDataOperateType,
  editingData: editingDictData,
  handleAdd: handleAddDictDataBase,
  handleEdit: handleEditDictData,
  checkedRowKeys: checkedDictDataRowKeys,
  onBatchDeleted: onBatchDeletedDictData,
  onDeleted: onDeletedDictData
} = useTableOperate<Api.Dict.DictData>(dictData, 'id', getDictData);

function selectType(item: Api.Dict.DictType) {
  currentTypeId.value = item.id;
}

function openEditType(id: number) {
  currentTypeId.value = id;
  handleEditDictType(id);
}

async function handleDeleteType(id: number) {
  const { error } = await fetchDeleteDictType(id);

  if (error) {
    return;
  }

  if (currentTypeId.value === id) {
    currentTypeId.value = null;
  }

  await onDeletedDictType();
}

function handleDictDataSelectionChange(rows: Api.Dict.DictData[]) {
  checkedDictDataRowKeys.value = rows.map(item => String(item.id));
}

function handleAddDictData() {
  if (!currentType.value) {
    window.$message?.warning($t('page.system.dict.emptyType'));
    return;
  }

  handleAddDictDataBase();
}

function editDictData(id: number) {
  handleEditDictData(id);
}

async function handleDeleteSingleDictData(id: number) {
  const { error } = await fetchDeleteDictData(id);

  if (error) {
    return;
  }

  await onDeletedDictData();
}

async function handleBatchDeleteDictDataRows() {
  const ids = checkedDictDataRowKeys.value.map(item => Number(item));

  if (!ids.length) {
    return;
  }

  const { error } = await fetchBatchDeleteDictData(ids);

  if (error) {
    return;
  }

  await onBatchDeletedDictData();
}

async function handleSearchDictType() {
  dictTypeSearchParams.pageNo = 1;
  await getDictTypeList();
}

async function resetDictTypeSearchParams() {
  const pageSize = dictTypeSearchParams.pageSize;

  Object.assign(dictTypeSearchParams, getInitDictTypeSearchParams(), { pageSize });

  await getDictTypeList();
}

async function handleSearchDictData() {
  dictDataSearchParams.pageNo = 1;
  await getDictDataByPage(1);
}

async function resetDictDataSearchParams() {
  const pageSize = dictDataSearchParams.pageSize;

  Object.assign(dictDataSearchParams, getInitDictDataSearchParams(), { pageSize });

  await getDictDataByPage(1);
}

async function handleTypeSubmitted() {
  await getDictTypeList();
}

async function handleDataSubmitted() {
  await getDictData();
}

watch(
  currentTypeCode,
  async (value, oldValue) => {
    if (value === oldValue) {
      return;
    }

    // 左侧切换类型时，右侧查询条件只保留分页大小，其余条件全部重置。
    const pageSize = dictDataSearchParams.pageSize;

    Object.assign(dictDataSearchParams, getInitDictDataSearchParams(), {
      pageSize,
      dictType: value || undefined
    });

    await getDictDataByPage(1);
  },
  { immediate: true }
);

onMounted(() => {
  getDictTypeList();
});
</script>

<template>
  <div
    class="min-h-560px gap-16px overflow-hidden xl:grid xl:grid-cols-[360px_minmax(0,1fr)] lt-xl:flex lt-xl:flex-col lt-xl:overflow-auto"
  >
    <div class="flex-col-stretch gap-16px xl:min-h-0">
      <DictTypeSearch
        v-model:model="dictTypeSearchParams"
        @reset="resetDictTypeSearchParams"
        @search="handleSearchDictType"
      />
      <ElCard v-loading="typeLoading" class="card-wrapper xl:flex-1-hidden" body-class="dict-type-card-body">
        <template #header>
          <div class="flex items-center justify-between gap-12px py-2px">
            <div class="flex items-center gap-10px">
              <p>{{ $t('page.system.dict.typeTitle') }}</p>
              <ElTag effect="plain">{{ typeTotal }}</ElTag>
            </div>
            <ElButton type="primary" plain size="small" @click="handleAddDictType">
              <template #icon>
                <icon-ic-round-plus class="text-icon" />
              </template>
              {{ $t('common.add') }}
            </ElButton>
          </div>
        </template>

        <ElScrollbar class="flex-1 px-10px py-10px">
          <div v-if="typeList.length" class="flex flex-col gap-8px">
            <div
              v-for="item in typeList"
              :key="item.id"
              class="group w-full cursor-pointer border rounded-12px px-12px py-10px text-left transition-all duration-200"
              :class="
                item.id === currentTypeId
                  ? 'border-primary bg-primary/6 shadow-sm shadow-primary/10'
                  : 'border-[#ebeef5] bg-white hover:border-primary/45 hover:bg-[#fafcff]'
              "
              @click="selectType(item)"
            >
              <div class="flex items-center gap-10px">
                <icon-ep-success-filled v-if="item.status === 0" class="shrink-0 text-16px text-[#67c23a]" />
                <icon-ep-remove-filled v-else class="shrink-0 text-16px text-[#c0c4cc]" />
                <ElTooltip :content="`${item.name} / ${item.type}`" placement="top-start">
                  <div class="min-w-0 flex-1 truncate text-14px text-[#1f2329] font-600">
                    <span class="truncate align-middle">{{ item.name }}</span>
                    <span class="mx-6px align-middle text-[#c0c4cc]">/</span>
                    <span class="align-middle text-13px text-[#7d8592] font-500">{{ item.type }}</span>
                  </div>
                </ElTooltip>
                <div
                  class="flex shrink-0 items-center gap-2 opacity-75 transition-opacity duration-200 group-hover:opacity-100"
                  @click.stop
                >
                  <ElTooltip :content="$t('common.edit')">
                    <ElButton link type="primary" @click="openEditType(item.id)">
                      <icon-mdi-pencil-outline class="text-16px" />
                    </ElButton>
                  </ElTooltip>
                  <ElPopconfirm :title="$t('common.confirmDelete')" @confirm="handleDeleteType(item.id)">
                    <template #reference>
                      <span class="inline-flex">
                        <ElTooltip :content="$t('common.delete')">
                          <ElButton link type="danger">
                            <icon-mdi-delete-outline class="text-16px" />
                          </ElButton>
                        </ElTooltip>
                      </span>
                    </template>
                  </ElPopconfirm>
                </div>
              </div>
            </div>
          </div>

          <div v-else class="min-h-260px flex items-center justify-center">
            <ElEmpty :description="$t('common.noData')" />
          </div>
        </ElScrollbar>
      </ElCard>
    </div>

    <div class="flex-col-stretch gap-16px xl:min-h-0">
      <DictDataSearch
        v-model:model="dictDataSearchParams"
        :disabled="!currentType"
        @reset="resetDictDataSearchParams"
        @search="handleSearchDictData"
      />
      <ElCard class="card-wrapper xl:flex-1-hidden" body-class="dict-data-card-body">
        <template #header>
          <div class="flex items-center justify-between gap-12px">
            <div class="flex flex-wrap items-center gap-8px">
              <p>{{ $t('page.system.dict.dataTitle') }}</p>
              <ElTag v-if="currentType" type="primary" effect="light">
                {{ currentType.name }}
              </ElTag>
              <span v-if="currentType" class="rounded-full bg-[#f4f6f8] px-10px py-4px text-12px text-[#606266]">
                {{ currentType.type }}
              </span>
              <span v-else class="text-14px text-[#909399]">{{ $t('page.system.dict.emptyType') }}</span>
            </div>
            <TableHeaderOperation
              v-model:columns="dictDataColumnChecks"
              :disabled-delete="checkedDictDataRowKeys.length === 0 || !currentType"
              :loading="dictDataLoading"
              @refresh="getDictData"
            >
              <template #default>
                <ElButton plain type="primary" :disabled="!currentType" @click="handleAddDictData">
                  <template #icon>
                    <icon-ic-round-plus class="text-icon" />
                  </template>
                  {{ $t('common.add') }}
                </ElButton>
                <ElPopconfirm :title="$t('common.confirmDelete')" @confirm="handleBatchDeleteDictDataRows">
                  <template #reference>
                    <ElButton type="danger" plain :disabled="checkedDictDataRowKeys.length === 0 || !currentType">
                      <template #icon>
                        <icon-ic-round-delete class="text-icon" />
                      </template>
                      {{ $t('common.batchDelete') }}
                    </ElButton>
                  </template>
                </ElPopconfirm>
              </template>
            </TableHeaderOperation>
          </div>
        </template>

        <template v-if="currentType">
          <div class="flex-1">
            <ElTable
              v-loading="dictDataLoading"
              height="100%"
              border
              row-key="id"
              :data="dictData"
              @selection-change="handleDictDataSelectionChange"
            >
              <ElTableColumn v-for="col in dictDataColumns" :key="String(col.prop)" v-bind="col" />
            </ElTable>
          </div>
          <div class="mt-20px flex justify-end">
            <ElPagination
              v-if="dictDataPagination.total"
              layout="total,prev,pager,next,sizes"
              v-bind="dictDataPagination"
              @current-change="dictDataPagination['current-change']"
              @size-change="dictDataPagination['size-change']"
            />
          </div>
        </template>

        <div v-else class="h-full flex items-center justify-center">
          <ElEmpty :description="$t('page.system.dict.emptyType')" />
        </div>
      </ElCard>
    </div>

    <DictTypeOperateModal
      v-model:visible="dictTypeModalVisible"
      :operate-type="dictTypeOperateType"
      :row-data="editingDictTypeData"
      @submitted="handleTypeSubmitted"
    />
    <DictDataOperateModal
      v-model:visible="dictDataModalVisible"
      :operate-type="dictDataOperateType"
      :row-data="editingDictData"
      :current-type="currentType"
      @submitted="handleDataSubmitted"
    />
  </div>
</template>

<style lang="scss" scoped>
:deep(.dict-type-card-body) {
  height: calc(100% - 56px);
  display: flex;
  flex-direction: column;
  padding: 0;
}

:deep(.dict-data-card-body) {
  height: calc(100% - 56px);
  display: flex;
  flex-direction: column;
}
</style>
