<script setup lang="tsx">
import { computed, nextTick, reactive, ref, watch } from 'vue';
import type { TableInstance } from 'element-plus';
import { ElTag } from 'element-plus';
import { useBoolean } from '@sa/hooks';
import {
  commonStatusRecord,
  menuRouteKindRecord,
  menuTypeRecord,
  objectTypeRecord,
  scopeTypeRecord
} from '@/constants/business';
import { fetchBatchDeleteMenu, fetchDeleteMenu, fetchGetMenuList } from '@/service/api';
import { useUITable } from '@/hooks/common/table';
import { $t } from '@/locales';
import { buildMenuTree } from '@/views/system/shared/menu-tree';
import MenuContextPanel from './modules/menu-context-panel.vue';
import MenuIconCell from './modules/menu-icon-cell';
import MenuOperateDialog, { type OperateType } from './modules/menu-operate-dialog.vue';
import MenuOperateCell from './modules/menu-operate-cell';
import MenuSearch from './modules/menu-search.vue';

defineOptions({ name: 'MenuManage' });

function getInitSearchParams(): Api.SystemManage.MenuSearchParams {
  return {
    name: undefined,
    status: undefined
  };
}

function getMenuTypeTagType(type: Api.SystemManage.MenuType): UI.ThemeColor {
  const tagMap: Record<Api.SystemManage.MenuType, UI.ThemeColor> = {
    1: 'info',
    2: 'primary',
    3: 'warning'
  };

  return tagMap[type];
}

function getStatusTagType(status: Api.SystemManage.CommonStatus): UI.ThemeColor {
  return status === 0 ? 'success' : 'warning';
}

function getStatusLabel(status: Api.SystemManage.CommonStatus) {
  return $t(commonStatusRecord[status]);
}

function getMenuTypeLabel(type: Api.SystemManage.MenuType, currentScopeType: Api.SystemManage.ScopeType) {
  if (currentScopeType === 'object') {
    if (type === 2) {
      return $t('page.system.menu.type.navigation');
    }

    if (type === 3) {
      return $t('page.system.menu.type.actionButton');
    }
  }

  return $t(menuTypeRecord[type]);
}

function getRouteKindLabel(routeKind?: Api.SystemManage.MenuRouteKind | null) {
  if (!routeKind) {
    return '--';
  }

  return $t(menuRouteKindRecord[routeKind]);
}

const searchParams = reactive(getInitSearchParams());
const flatMenuList = ref<Api.SystemManage.Menu[]>([]);
const scopeType = ref<Api.SystemManage.ScopeType>('global');
const objectType = ref<Api.SystemManage.ObjectType>('product');

const isObjectScope = computed(() => scopeType.value === 'object');

function getCurrentScopeParams(): Api.SystemManage.ScopeQueryParams {
  if (scopeType.value === 'object') {
    return {
      scopeType: 'object',
      objectType: objectType.value
    };
  }

  return {
    scopeType: 'global'
  };
}

const { columns, columnChecks, data, loading, getData } = useUITable({
  api: () =>
    fetchGetMenuList({
      ...searchParams,
      ...getCurrentScopeParams()
    }),
  transform: response => {
    if (!response.error) {
      flatMenuList.value = response.data;
      return buildMenuTree(response.data);
    }

    flatMenuList.value = [];
    return [];
  },
  columns: () => [
    { prop: 'selection', type: 'selection', width: 48 },
    { prop: 'name', label: $t('page.system.menu.menuName'), minWidth: 240, showOverflowTooltip: true },
    {
      prop: 'type',
      label: $t('page.system.menu.menuType'),
      width: 108,
      align: 'center',
      formatter: row => <ElTag type={getMenuTypeTagType(row.type)}>{getMenuTypeLabel(row.type, scopeType.value)}</ElTag>
    },
    {
      prop: 'status',
      label: $t('page.system.menu.menuStatus'),
      width: 110,
      align: 'center',
      formatter: row => <ElTag type={getStatusTagType(row.status)}>{getStatusLabel(row.status)}</ElTag>
    },
    {
      prop: 'icon',
      label: $t('page.system.menu.icon'),
      width: 88,
      align: 'center',
      formatter: row => {
        return <MenuIconCell icon={row.icon ?? ''} />;
      }
    },
    { prop: 'sort', label: $t('page.system.menu.order'), width: 88, align: 'center' },
    { prop: 'permission', label: $t('page.system.menu.permission'), minWidth: 180, showOverflowTooltip: true },
    { prop: 'path', label: $t('page.system.menu.routePath'), minWidth: 160, showOverflowTooltip: true },
    {
      prop: 'routeKind',
      label: $t('page.system.menu.routeKind'),
      width: 116,
      formatter: row => getRouteKindLabel(row.routeKind)
    },
    { prop: 'component', label: $t('page.system.menu.component'), minWidth: 180, showOverflowTooltip: true },
    { prop: 'componentName', label: $t('page.system.menu.componentName'), minWidth: 160, showOverflowTooltip: true },
    {
      prop: 'operate',
      label: $t('common.operate'),
      width: 196,
      align: 'center',
      fixed: 'right',
      formatter: row => (
        <MenuOperateCell row={row} onEdit={openEdit} onAddChild={openAddChild} onDelete={handleDeleteAction} />
      )
    }
  ]
});

const { bool: visible, setTrue: openModal, setFalse: closeModal } = useBoolean();
const operateType = ref<OperateType>('add');
const editingData = ref<Api.SystemManage.Menu | null>(null);
const checkedRowKeys = ref<string[]>([]);
const tableRef = ref<TableInstance>();
const tableRenderKey = ref(0);

const allMenus = computed(() => flatMenuList.value);
const currentScopeLabel = computed(() => $t(scopeTypeRecord[scopeType.value]));
const currentObjectTypeLabel = computed(() => {
  return objectType.value ? $t(objectTypeRecord[objectType.value]) : '';
});
const currentContextTagLabel = computed(() => {
  return isObjectScope.value && currentObjectTypeLabel.value
    ? `${currentScopeLabel.value} / ${currentObjectTypeLabel.value}`
    : currentScopeLabel.value;
});
const currentTableTitle = computed(() => {
  return $t(isObjectScope.value ? 'page.system.menu.objectResourceTitle' : 'page.system.menu.globalResourceTitle');
});
const expandedRowKeys = computed(() => {
  const firstRootMenu = data.value[0];

  return firstRootMenu ? [String(firstRootMenu.id)] : [];
});

function handleSelectionChange(rows: Api.SystemManage.Menu[]) {
  checkedRowKeys.value = rows.map(item => item.id);
}

async function reloadTable() {
  checkedRowKeys.value = [];
  await getData();
  tableRenderKey.value += 1;
  await nextTick();
  tableRef.value?.clearSelection();
}

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

function openAddChild(item: Api.SystemManage.Menu) {
  operateType.value = 'addChild';
  editingData.value = item;
  openModal();
}

function openEdit(item: Api.SystemManage.Menu) {
  operateType.value = 'edit';
  editingData.value = item;
  openModal();
}

async function handleDelete(id: string) {
  const { error } = await fetchDeleteMenu(id);

  if (error) {
    return;
  }

  window.$message?.success($t('common.deleteSuccess'));
  await reloadTable();
}

async function handleDeleteAction(row: Api.SystemManage.Menu) {
  try {
    await window.$messageBox?.confirm($t('common.confirmDelete'), $t('common.warning'), {
      confirmButtonText: $t('common.confirm'),
      cancelButtonText: $t('common.cancel'),
      type: 'warning'
    });
  } catch {
    return;
  }

  await handleDelete(row.id);
}

async function handleBatchDelete() {
  if (!checkedRowKeys.value.length) {
    return;
  }

  const { error } = await fetchBatchDeleteMenu(checkedRowKeys.value);

  if (error) {
    return;
  }

  window.$message?.success($t('common.deleteSuccess'));
  await reloadTable();
}

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

async function handleSearch() {
  await reloadTable();
}

async function handleSubmitted() {
  closeModal();
  await reloadTable();
}

watch(scopeType, value => {
  if (value === 'object' && !objectType.value) {
    objectType.value = 'product';
  }
});

let contextChangeToken = 0;

watch([scopeType, objectType], async ([nextScope, nextObject], [prevScope, prevObject]) => {
  if (nextScope === prevScope && nextObject === prevObject) {
    return;
  }

  contextChangeToken += 1;
  const token = contextChangeToken;
  await nextTick();

  if (token !== contextChangeToken) {
    return;
  }

  Object.assign(searchParams, getInitSearchParams());
  operateType.value = 'add';
  editingData.value = null;
  closeModal();
  await reloadTable();
});
</script>

<template>
  <div class="min-h-560px flex-col-stretch gap-16px overflow-hidden lt-sm:overflow-auto">
    <MenuContextPanel
      v-model:scope-type="scopeType"
      v-model:object-type="objectType"
      :total="flatMenuList.length"
      :loading="loading"
    />

    <MenuSearch v-model:model="searchParams" @reset="resetSearchParams" @search="handleSearch" />

    <ElCard class="card-wrapper sm:flex-1-hidden" body-class="menu-card-body">
      <template #header>
        <div class="menu-card-header">
          <div class="min-w-0 flex-1">
            <div class="flex flex-wrap items-center gap-8px">
              <p class="truncate text-16px font-600">{{ currentTableTitle }}</p>
              <ElTag effect="plain" :type="isObjectScope ? 'success' : 'primary'">{{ currentContextTagLabel }}</ElTag>
              <ElTag effect="plain">{{ flatMenuList.length }}</ElTag>
            </div>
          </div>

          <TableHeaderOperation
            v-model:columns="columnChecks"
            :disabled-delete="checkedRowKeys.length === 0"
            :loading="loading"
            @refresh="reloadTable"
          >
            <template #default>
              <ElButton plain type="primary" @click="openAdd">
                <template #icon>
                  <icon-ic-round-plus class="text-icon" />
                </template>
                {{ $t('common.add') }}
              </ElButton>
              <ElPopconfirm :title="$t('common.confirmDelete')" @confirm="handleBatchDelete">
                <template #reference>
                  <ElButton type="danger" plain :disabled="checkedRowKeys.length === 0">
                    <template #icon>
                      <icon-ic-round-delete class="text-icon" />
                    </template>
                    {{ $t('common.batchDelete') }}
                  </ElButton>
                </template>
              </ElPopconfirm>
            </template>
          </TableHeaderOperation>
        </div>
      </template>

      <ElTable
        :key="tableRenderKey"
        ref="tableRef"
        v-loading="loading"
        height="100%"
        border
        row-key="id"
        :expand-row-keys="expandedRowKeys"
        :data="data"
        class="sm:h-full"
        @selection-change="handleSelectionChange"
      >
        <ElTableColumn v-for="col in columns" :key="String(col.prop)" v-bind="col" />
      </ElTable>
    </ElCard>

    <MenuOperateDialog
      v-model:visible="visible"
      :operate-type="operateType"
      :row-data="editingData"
      :all-menus="allMenus"
      :scope-type="scopeType"
      :object-type="objectType"
      @submitted="handleSubmitted"
    />
  </div>
</template>

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

.menu-card-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

@media (width <= 768px) {
  .menu-card-header {
    flex-direction: column;
    align-items: stretch;
  }
}
</style>
