<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import type { ElegantConstRoute } from '@elegant-router/types';
import { commonStatusOptions, menuRouteKindOptions } from '@/constants/business';
import { objectContextDomainConfigs } from '@/constants/object-context';
import { fetchCreateMenu, fetchGetMenu, fetchUpdateMenu } from '@/service/api';
import { useForm, useFormRules } from '@/hooks/common/form';
import { createStaticRoutes } from '@/router/routes';
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
import BusinessFormSection from '@/components/custom/business-form-section.vue';
import { $t } from '@/locales';
import { buildMenuTree, collectDescendantIds } from '@/views/system/shared/menu-tree';
import frontendPageResourceManifest from '../../../../../docs/frontend-page-resource-manifest.json';
import { menuIconGroups } from './icon-options';

defineOptions({ name: 'MenuOperateDialog' });

export type OperateType = UI.TableOperateType | 'addChild';

interface Props {
  operateType: OperateType;
  rowData?: Api.SystemManage.Menu | null;
  allMenus: Api.SystemManage.Menu[];
  scopeType: Api.SystemManage.ScopeType;
  objectType?: Api.SystemManage.ObjectType;
}

const props = defineProps<Props>();

interface Emits {
  (e: 'submitted'): void;
}

const emit = defineEmits<Emits>();

const visible = defineModel<boolean>('visible', {
  default: false
});

type Model = Api.SystemManage.SaveMenuParams & {
  pageResourcePath: string;
  iframeUrl: string;
  externalUrl: string;
  redirectTarget: string;
};

type RuleFormItem = {
  message: string;
  trigger?: 'blur' | 'change';
  validator?: App.Global.FormRule['validator'];
};

type ParentTreeOption = {
  value: string;
  label: string;
  disabled?: boolean;
  children?: ParentTreeOption[];
};

type RouteBindingItem = {
  name: string;
  path: string;
  component: string;
  title: string;
  keepAlive: boolean;
  props: Record<string, unknown> | null;
};

const DIRECTORY_COMPONENT = frontendPageResourceManifest.rules.directoryComponent;
const IFRAME_COMPONENT = 'view.iframe-page';

const pageResourceItems = frontendPageResourceManifest.items
  .filter(item => item.pageType === 'leaf')
  .slice()
  .sort((prev, next) => prev.path.localeCompare(next.path));

const globalRouteBindingItems: RouteBindingItem[] = pageResourceItems.map(item => ({
  name: item.name,
  path: item.path,
  component: item.component,
  title: item.title || item.name,
  keepAlive: Boolean(item.keepAlive),
  props: (item.props as Record<string, unknown> | null) ?? null
}));

const staticAuthRoutes = createStaticRoutes().authRoutes;

const { formRef, validate } = useForm();
const { createRequiredRule } = useFormRules();

const submitting = ref(false);
const detailLoading = ref(false);
const initializingModel = ref(false);

const isEdit = computed(() => props.operateType === 'edit');
const isAddChild = computed(() => props.operateType === 'addChild');
const isObjectScope = computed(() => props.scopeType === 'object');

const title = computed(() => {
  const titleMap: Record<OperateType, string> = {
    add: $t('page.system.menu.addMenu'),
    addChild: $t('page.system.menu.addChildMenu'),
    edit: $t('page.system.menu.editMenu')
  };

  return titleMap[props.operateType];
});

const model = ref(createDefaultModel());

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

  return {
    scopeType: 'global'
  };
}

function createDefaultModel(): Model {
  return {
    name: '',
    permission: '',
    scopeType: props.scopeType,
    objectType: props.scopeType === 'object' ? props.objectType : undefined,
    type: 2,
    sort: 0,
    parentId: '0',
    path: '',
    icon: '',
    component: '',
    componentName: '',
    routeKind: 'view',
    routePropsJson: '',
    status: 0,
    visible: true,
    keepAlive: true,
    alwaysShow: false,
    pageResourcePath: '',
    iframeUrl: '',
    externalUrl: '',
    redirectTarget: ''
  };
}

function normalizeRoutePart(value?: string | null) {
  return String(value ?? '')
    .trim()
    .replace(/\\/g, '/')
    .replace(/^\/+|\/+$/g, '')
    .replace(/\/{2,}/g, '/');
}

function toAbsoluteRoutePath(value?: string | null) {
  const normalized = normalizeRoutePart(value);

  return normalized ? `/${normalized}` : '';
}

function joinRoutePaths(parentFullPath?: string | null, currentPath?: string | null) {
  const parent = toAbsoluteRoutePath(parentFullPath);
  const current = normalizeRoutePart(currentPath);

  if (!parent) {
    return current ? `/${current}` : '';
  }

  return current ? `${parent}/${current}`.replace(/\/{2,}/g, '/') : parent;
}

function getRelativeRoutePath(fullPath?: string | null, parentFullPath?: string | null) {
  const normalizedFullPath = toAbsoluteRoutePath(fullPath);
  const normalizedParentPath = toAbsoluteRoutePath(parentFullPath);

  if (!normalizedFullPath) {
    return '';
  }

  if (!normalizedParentPath) {
    return normalizedFullPath.slice(1);
  }

  const prefix = `${normalizedParentPath}/`;

  if (!normalizedFullPath.startsWith(prefix)) {
    return '';
  }

  return normalizedFullPath.slice(prefix.length);
}

function buildComponentNameFromFullPath(fullPath?: string | null) {
  const normalizedFullPath = toAbsoluteRoutePath(fullPath);

  if (!normalizedFullPath) {
    return '';
  }

  return normalizedFullPath
    .split('/')
    .filter(Boolean)
    .map(segment => segment.replace(/:.+$/, '').replace(/[^\w-]/g, '_'))
    .filter(Boolean)
    .join('_');
}

function isPathMatchedByPrefix(path: string, prefix: string) {
  const normalizedPath = toAbsoluteRoutePath(path);
  const normalizedPrefix = toAbsoluteRoutePath(prefix);

  return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}/`);
}

function collectObjectRouteBindingItems(
  routes: ElegantConstRoute[],
  config: App.ObjectContext.DomainConfig
): RouteBindingItem[] {
  return routes.flatMap(route => {
    if (route.children?.length) {
      return collectObjectRouteBindingItems(route.children, config);
    }

    const routePath = toAbsoluteRoutePath(route.path);

    if (!routePath || route.name === config.entryRouteKey || routePath === config.entryRoutePath) {
      return [];
    }

    if (!config.routePathPrefixes.some(prefix => isPathMatchedByPrefix(routePath, prefix))) {
      return [];
    }

    const component = String(route.component ?? '').trim();

    if (!component.includes('view.')) {
      return [];
    }

    return [
      {
        name: String(route.name || routePath),
        path: routePath,
        component,
        title: route.meta?.i18nKey ? $t(route.meta.i18nKey) : String(route.meta?.title || route.name || routePath),
        keepAlive: Boolean(route.meta?.keepAlive),
        props:
          route.props && typeof route.props === 'object' && !Array.isArray(route.props)
            ? (route.props as Record<string, unknown>)
            : null
      }
    ];
  });
}

function parseRoutePropsJson(value?: string | null) {
  const text = String(value ?? '').trim();

  if (!text) {
    return null;
  }

  try {
    const parsed = JSON.parse(text);

    if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
      return null;
    }

    return parsed as Record<string, unknown>;
  } catch {
    return null;
  }
}

function stringifyRouteProps(value: Record<string, unknown> | null | undefined) {
  return value ? JSON.stringify(value) : '';
}

function getRoutePropText(routeProps: Record<string, unknown> | null, key: string) {
  const value = routeProps?.[key];

  return typeof value === 'string' ? value : '';
}

function extractViewComponent(component?: string | null) {
  const normalizedComponent = String(component ?? '').trim();

  if (!normalizedComponent) {
    return '';
  }

  const componentParts = normalizedComponent.split('$');

  return componentParts[componentParts.length - 1] ?? '';
}

function getNullableText(value?: string | null) {
  return value?.trim() || null;
}

function getMenuFullPath(menuId: string) {
  if (!menuId || menuId === '0') {
    return '';
  }

  const menuMap = new Map(props.allMenus.map(item => [item.id, item]));
  const visitedIds = new Set<string>();
  const pathSegments: string[] = [];
  let currentMenu = menuMap.get(menuId);

  while (currentMenu && !visitedIds.has(currentMenu.id)) {
    visitedIds.add(currentMenu.id);

    const currentPath = normalizeRoutePart(currentMenu.path);

    if (currentPath) {
      pathSegments.unshift(currentPath);
    }

    if (currentMenu.parentId === '0') {
      break;
    }

    currentMenu = menuMap.get(currentMenu.parentId);
  }

  return pathSegments.length ? `/${pathSegments.join('/')}` : '';
}

function getMenuFullPathByData(data: Pick<Api.SystemManage.Menu, 'parentId' | 'path'>) {
  return joinRoutePaths(getMenuFullPath(data.parentId), data.path);
}

function resolveRouteBindingPath(data: Api.SystemManage.Menu) {
  const viewComponent = extractViewComponent(data.component);
  const objectDomainConfig = props.objectType
    ? objectContextDomainConfigs.find(config => config.objectType === props.objectType) || null
    : null;
  const candidateItems =
    isObjectScope.value && objectDomainConfig
      ? collectObjectRouteBindingItems(staticAuthRoutes, objectDomainConfig)
      : globalRouteBindingItems;

  if (viewComponent) {
    const matchedByComponent = candidateItems.find(item => item.component === viewComponent);

    if (matchedByComponent) {
      return matchedByComponent.path;
    }
  }

  const fullPath = getMenuFullPathByData(data);

  return candidateItems.find(item => item.path === fullPath)?.path ?? '';
}

const currentMenuId = computed(() => props.rowData?.id ?? '0');
const currentParentFullPath = computed(() => getMenuFullPath(model.value.parentId));
const isButton = computed(() => model.value.type === 3);
const isMenu = computed(() => model.value.type === 2);
const currentObjectDomainConfig = computed(() => {
  if (!props.objectType) {
    return null;
  }

  return objectContextDomainConfigs.find(config => config.objectType === props.objectType) || null;
});

const objectRouteBindingItems = computed<RouteBindingItem[]>(() => {
  if (!isObjectScope.value || !currentObjectDomainConfig.value) {
    return [];
  }

  return collectObjectRouteBindingItems(staticAuthRoutes, currentObjectDomainConfig.value);
});

const routeBindingItems = computed<RouteBindingItem[]>(() => {
  return isObjectScope.value ? objectRouteBindingItems.value : globalRouteBindingItems;
});

const routeBindingMap = computed(() => new Map(routeBindingItems.value.map(item => [item.path, item])));
const routeBindingOptions = computed(() =>
  routeBindingItems.value.map(item => ({
    value: item.path,
    label: `${item.title || item.name} (${item.path})`
  }))
);

const selectedRouteBinding = computed(() => routeBindingMap.value.get(model.value.pageResourcePath) ?? null);
const routeBindingFieldLabel = computed(() =>
  isObjectScope.value ? $t('page.system.menu.boundRoute') : $t('page.system.menu.pageResource')
);
const routeBindingFieldPlaceholder = computed(() =>
  isObjectScope.value ? $t('page.system.menu.form.boundRoute') : $t('page.system.menu.form.pageResource')
);
const routeBindingFieldTip = computed(() =>
  isObjectScope.value ? $t('page.system.menu.tips.boundRoute') : $t('page.system.menu.tips.pageResource')
);

const menuTypeRadioOptions = computed(() => {
  if (!isObjectScope.value) {
    return [
      { value: 1 as Api.SystemManage.MenuType, label: 'page.system.menu.type.directory', disabled: false },
      { value: 2 as Api.SystemManage.MenuType, label: 'page.system.menu.type.menu', disabled: false },
      { value: 3 as Api.SystemManage.MenuType, label: 'page.system.menu.type.button', disabled: false }
    ];
  }

  const options = [
    { value: 2 as Api.SystemManage.MenuType, label: 'page.system.menu.type.navigation', disabled: false },
    { value: 3 as Api.SystemManage.MenuType, label: 'page.system.menu.type.actionButton', disabled: false }
  ];

  if (isEdit.value && model.value.type === 1) {
    return [
      { value: 1 as Api.SystemManage.MenuType, label: 'page.system.menu.type.directory', disabled: true },
      ...options
    ];
  }

  return options;
});

const showRouteFields = computed(() => !isButton.value);
const showRouteSection = computed(() => showRouteFields.value);
const showPermissionField = computed(() => isButton.value);
const showIconField = computed(() => showRouteFields.value);
const showRouteKindField = computed(() => showRouteFields.value && !isObjectScope.value);

const isDirectoryRoute = computed(() => model.value.routeKind === 'dir');
const isViewRoute = computed(() => model.value.routeKind === 'view');
const isSingleRoute = computed(() => model.value.routeKind === 'single');
const isIframeRoute = computed(() => model.value.routeKind === 'iframe');
const isExternalRoute = computed(() => model.value.routeKind === 'external');
const isRedirectRoute = computed(() => model.value.routeKind === 'redirect');

const showPageResourceField = computed(() => isViewRoute.value);
const showReadonlyPathField = computed(() => isViewRoute.value);
const showEditablePathField = computed(() => showRouteFields.value && !isViewRoute.value);
const showReadonlyComponentField = computed(() => isDirectoryRoute.value || isViewRoute.value || isIframeRoute.value);
const showManualComponentField = computed(() => isSingleRoute.value);
const showReadonlyComponentNameField = computed(
  () =>
    isDirectoryRoute.value || isViewRoute.value || isIframeRoute.value || isExternalRoute.value || isRedirectRoute.value
);
const showManualComponentNameField = computed(() => isSingleRoute.value);
const showIframeUrlField = computed(() => isIframeRoute.value);
const showExternalUrlField = computed(() => isExternalRoute.value);
const showRedirectTargetField = computed(() => isRedirectRoute.value);
const showReadonlyRouteProps = computed(() => isIframeRoute.value);
const showRoutePropsEditor = computed(() => isSingleRoute.value);
const canKeepAlive = computed(
  () => !isObjectScope.value && isMenu.value && !isExternalRoute.value && !isRedirectRoute.value
);
const showDisplaySection = computed(() => canKeepAlive.value);

const keepAliveSwitch = computed({
  get: () => Boolean(model.value.keepAlive),
  set: value => {
    model.value.keepAlive = value;
  }
});

const iconFieldValue = computed({
  get: () => model.value.icon ?? '',
  set: value => {
    model.value.icon = value;
  }
});

const routeKindSelectOptions = computed(() => {
  if (isObjectScope.value && model.value.type === 2) {
    return menuRouteKindOptions.filter(item => item.value === 'view');
  }

  if (model.value.type === 1) {
    return menuRouteKindOptions.filter(item => item.value === 'dir');
  }

  return menuRouteKindOptions.filter(item => item.value !== 'dir');
});

const routeKindTipKeyRecord: Record<Api.SystemManage.MenuRouteKind, App.I18n.I18nKey> = {
  dir: 'page.system.menu.tips.routeKindItems.directory',
  view: 'page.system.menu.tips.routeKindItems.view',
  single: 'page.system.menu.tips.routeKindItems.single',
  iframe: 'page.system.menu.tips.routeKindItems.iframe',
  external: 'page.system.menu.tips.routeKindItems.external',
  redirect: 'page.system.menu.tips.routeKindItems.redirect'
};

const routeKindTipItems = computed(() =>
  routeKindSelectOptions.value.map(item => ({
    value: item.value,
    label: $t(item.label),
    description: $t(routeKindTipKeyRecord[item.value])
  }))
);

const displayRoutePath = computed(() => {
  if (isObjectScope.value && isMenu.value) {
    return selectedRouteBinding.value?.path || toAbsoluteRoutePath(model.value.path);
  }

  return joinRoutePaths(currentParentFullPath.value, model.value.path);
});

const hasCompatibleViewRouteData = computed(() =>
  Boolean(getNullableText(model.value.component) && getNullableText(model.value.path))
);

const rules = computed(() => {
  const permissionRule: RuleFormItem = {
    message: $t('page.system.menu.form.permission'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showPermissionField.value) {
        callback();
        return;
      }

      if (String(value ?? '').trim()) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.permission')));
    }
  };

  const pathRule: RuleFormItem = {
    message: $t('page.system.menu.form.path'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showEditablePathField.value) {
        callback();
        return;
      }

      if (String(value ?? '').trim()) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.path')));
    }
  };

  const pageResourceRule: RuleFormItem = {
    message: isObjectScope.value ? $t('page.system.menu.form.boundRoute') : $t('page.system.menu.form.pageResource'),
    trigger: 'change',
    validator: (_, value, callback) => {
      if (!showPageResourceField.value) {
        callback();
        return;
      }

      if (!String(value ?? '').trim()) {
        if (isEdit.value && hasCompatibleViewRouteData.value) {
          callback();
          return;
        }

        callback(
          new Error(
            isObjectScope.value ? $t('page.system.menu.form.boundRoute') : $t('page.system.menu.form.pageResource')
          )
        );
        return;
      }

      callback();
    }
  };

  const componentRule: RuleFormItem = {
    message: $t('page.system.menu.form.component'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showManualComponentField.value) {
        callback();
        return;
      }

      if (String(value ?? '').trim()) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.component')));
    }
  };

  const routeKindRule: RuleFormItem = {
    message: $t('page.system.menu.form.routeKind'),
    trigger: 'change',
    validator: (_, value, callback) => {
      if (!showRouteFields.value) {
        callback();
        return;
      }

      if (value) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.routeKind')));
    }
  };

  const iframeUrlRule: RuleFormItem = {
    message: $t('page.system.menu.form.iframeUrl'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showIframeUrlField.value) {
        callback();
        return;
      }

      if (String(value ?? '').trim()) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.iframeUrl')));
    }
  };

  const externalUrlRule: RuleFormItem = {
    message: $t('page.system.menu.form.externalUrl'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showExternalUrlField.value) {
        callback();
        return;
      }

      if (String(value ?? '').trim()) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.externalUrl')));
    }
  };

  const redirectTargetRule: RuleFormItem = {
    message: $t('page.system.menu.form.redirectTarget'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showRedirectTargetField.value) {
        callback();
        return;
      }

      if (String(value ?? '').trim()) {
        callback();
        return;
      }

      callback(new Error($t('page.system.menu.form.redirectTarget')));
    }
  };

  const routePropsJsonRule: RuleFormItem = {
    message: $t('page.system.menu.form.routePropsJson'),
    trigger: 'blur',
    validator: (_, value, callback) => {
      if (!showRoutePropsEditor.value) {
        callback();
        return;
      }

      const text = String(value ?? '').trim();

      if (!text) {
        callback();
        return;
      }

      try {
        JSON.parse(text);
        callback();
      } catch {
        callback(new Error($t('page.system.menu.form.routePropsJson')));
      }
    }
  };

  return {
    name: createRequiredRule($t('page.system.menu.form.menuName')),
    type: createRequiredRule($t('page.system.menu.form.menuType')),
    parentId: createRequiredRule($t('page.system.menu.form.parentId')),
    permission: permissionRule,
    sort: createRequiredRule($t('page.system.menu.form.sort')),
    status: createRequiredRule($t('page.system.menu.form.menuStatus')),
    path: pathRule,
    pageResourcePath: pageResourceRule,
    component: componentRule,
    routeKind: routeKindRule,
    iframeUrl: iframeUrlRule,
    externalUrl: externalUrlRule,
    redirectTarget: redirectTargetRule,
    routePropsJson: routePropsJsonRule
  } satisfies Record<string, App.Global.FormRule>;
});

const parentTreeOptions = computed<ParentTreeOption[]>(() => {
  const menuTree = buildMenuTree(props.allMenus);
  const descendantIds = currentMenuId.value !== '0' ? collectDescendantIds(menuTree, currentMenuId.value) : [];
  const disabledIds = new Set<string>([currentMenuId.value, ...descendantIds].filter(id => id !== '0'));

  const availableMenus = props.allMenus.filter(item => {
    if (item.type === 3) {
      return false;
    }

    if (!isObjectScope.value) {
      return true;
    }

    if (isMenu.value) {
      return item.type === 1;
    }

    return true;
  });
  const availableMenuTree = buildMenuTree(availableMenus);

  function mapTreeOptions(nodes: Api.SystemManage.Menu[]): ParentTreeOption[] {
    return nodes.map(node => ({
      value: node.id,
      label: node.name,
      disabled: disabledIds.has(node.id),
      children: node.children?.length ? mapTreeOptions(node.children) : undefined
    }));
  }

  return [
    {
      value: '0',
      label: $t('page.system.menu.topLevel'),
      children: mapTreeOptions(availableMenuTree)
    }
  ];
});

function filterParentTreeNode(value: string, data: { label?: string }) {
  const keyword = value.trim().toLowerCase();

  if (!keyword) {
    return true;
  }

  return String(data.label ?? '')
    .toLowerCase()
    .includes(keyword);
}

function closeModal() {
  visible.value = false;
}

function clearFormValidation() {
  formRef.value?.clearValidate();
}

function clearRouteFields() {
  model.value.path = '';
  model.value.icon = '';
  model.value.component = '';
  model.value.componentName = '';
  model.value.routeKind = null;
  model.value.routePropsJson = '';
  model.value.pageResourcePath = '';
  model.value.iframeUrl = '';
  model.value.externalUrl = '';
  model.value.redirectTarget = '';
}

function applyMenuTypePreset(type: Api.SystemManage.MenuType) {
  if (type === 1) {
    model.value.permission = '';
    model.value.routeKind = 'dir';
    model.value.keepAlive = false;
  }

  if (type === 2 && (isObjectScope.value || !model.value.routeKind || model.value.routeKind === 'dir')) {
    model.value.permission = '';
    model.value.routeKind = 'view';
  }

  if (type === 3) {
    clearRouteFields();
    model.value.keepAlive = false;
    model.value.alwaysShow = false;
  }
}

function getDefaultRouteKind(type: Api.SystemManage.MenuType) {
  if (isObjectScope.value && type === 2) {
    return 'view';
  }

  if (type === 1) {
    return 'dir';
  }

  if (type === 2) {
    return 'view';
  }

  return null;
}

function syncDirectoryRouteFields() {
  model.value.component = DIRECTORY_COMPONENT;
  model.value.componentName = buildComponentNameFromFullPath(
    joinRoutePaths(currentParentFullPath.value, model.value.path)
  );
  model.value.routePropsJson = '';
}

function syncViewRouteFields() {
  const routeBinding = selectedRouteBinding.value;

  if (!routeBinding) {
    return;
  }

  const nextPath = isObjectScope.value
    ? toAbsoluteRoutePath(routeBinding.path)
    : getRelativeRoutePath(routeBinding.path, currentParentFullPath.value) ||
      normalizeRoutePart(model.value.path) ||
      normalizeRoutePart(routeBinding.path).split('/').filter(Boolean).at(-1) ||
      '';

  model.value.path = nextPath;
  model.value.component = routeBinding.component;
  model.value.componentName = routeBinding.name;
  model.value.routePropsJson = stringifyRouteProps(routeBinding.props);
}

function syncIframeRouteFields() {
  model.value.component = IFRAME_COMPONENT;
  model.value.componentName = buildComponentNameFromFullPath(
    joinRoutePaths(currentParentFullPath.value, model.value.path)
  );
  model.value.routePropsJson = model.value.iframeUrl.trim()
    ? stringifyRouteProps({ url: model.value.iframeUrl.trim() })
    : '';
}

function syncExternalRouteFields() {
  model.value.component = '';
  model.value.componentName = buildComponentNameFromFullPath(
    joinRoutePaths(currentParentFullPath.value, model.value.path)
  );
  model.value.routePropsJson = model.value.externalUrl.trim()
    ? stringifyRouteProps({ url: model.value.externalUrl.trim() })
    : '';
}

function syncRedirectRouteFields() {
  model.value.component = '';
  model.value.componentName = buildComponentNameFromFullPath(
    joinRoutePaths(currentParentFullPath.value, model.value.path)
  );
  model.value.routePropsJson = model.value.redirectTarget.trim()
    ? stringifyRouteProps({ redirect: model.value.redirectTarget.trim() })
    : '';
}

function resetRouteKindFields(
  nextRouteKind?: Api.SystemManage.MenuRouteKind | null,
  previousRouteKind?: Api.SystemManage.MenuRouteKind | null
) {
  if (!nextRouteKind || nextRouteKind === previousRouteKind) {
    return;
  }

  model.value.pageResourcePath = '';
  model.value.iframeUrl = '';
  model.value.externalUrl = '';
  model.value.redirectTarget = '';
  model.value.component = '';
  model.value.componentName = '';
  model.value.routePropsJson = '';

  if (previousRouteKind === 'view' || nextRouteKind === 'view') {
    model.value.path = '';
  }

  if (nextRouteKind === 'dir' || nextRouteKind === 'external' || nextRouteKind === 'redirect') {
    model.value.keepAlive = false;
  }
}

function syncCurrentRouteFields() {
  if (!showRouteFields.value || !model.value.routeKind) {
    return;
  }

  if (isDirectoryRoute.value) {
    syncDirectoryRouteFields();
    return;
  }

  if (isViewRoute.value) {
    syncViewRouteFields();
    return;
  }

  if (isIframeRoute.value) {
    syncIframeRouteFields();
    return;
  }

  if (isExternalRoute.value) {
    syncExternalRouteFields();
    return;
  }

  if (isRedirectRoute.value) {
    syncRedirectRouteFields();
  }
}

function applyRouteBindingMeta(routeBinding: RouteBindingItem) {
  model.value.keepAlive = Boolean(routeBinding.keepAlive);
}

function mapMenuDetailToModel(data: Api.SystemManage.Menu): Model {
  const routeProps = parseRoutePropsJson(data.routePropsJson);

  return {
    name: data.name,
    permission: data.permission ?? '',
    scopeType: data.scopeType ?? props.scopeType,
    objectType: data.objectType || (props.scopeType === 'object' ? props.objectType : undefined),
    type: data.type,
    sort: data.sort,
    parentId: data.parentId,
    path: data.path ?? '',
    icon: data.icon ?? '',
    component: data.component ?? '',
    componentName: data.componentName ?? '',
    routeKind: data.routeKind ?? getDefaultRouteKind(data.type),
    routePropsJson: data.routePropsJson ?? '',
    status: data.status,
    visible: data.visible ?? true,
    keepAlive: data.keepAlive ?? false,
    alwaysShow: data.alwaysShow ?? false,
    pageResourcePath: data.routeKind === 'view' ? resolveRouteBindingPath(data) : '',
    iframeUrl: data.routeKind === 'iframe' ? getRoutePropText(routeProps, 'url') : '',
    externalUrl: data.routeKind === 'external' ? getRoutePropText(routeProps, 'url') : '',
    redirectTarget: data.routeKind === 'redirect' ? getRoutePropText(routeProps, 'redirect') : ''
  };
}

function getSubmitData(): Api.SystemManage.SaveMenuParams {
  const scopeData = getCurrentScopeParams();
  let submitPath: string | null = null;

  if (showRouteFields.value) {
    submitPath =
      isObjectScope.value && isMenu.value
        ? getNullableText(toAbsoluteRoutePath(model.value.path))
        : getNullableText(model.value.path);
  }

  return {
    ...scopeData,
    name: model.value.name.trim(),
    type: model.value.type,
    sort: model.value.sort,
    parentId: model.value.parentId,
    status: model.value.status,
    permission: isButton.value ? getNullableText(model.value.permission) : null,
    path: submitPath,
    icon: showIconField.value ? getNullableText(model.value.icon) : null,
    component: showRouteFields.value ? getNullableText(model.value.component) : null,
    componentName: showRouteFields.value ? getNullableText(model.value.componentName) : null,
    routeKind: showRouteFields.value ? (model.value.routeKind ?? null) : null,
    routePropsJson: showRouteFields.value ? getNullableText(model.value.routePropsJson) : null,
    visible: isButton.value ? false : Boolean(model.value.visible),
    keepAlive: showRouteFields.value ? Boolean(model.value.keepAlive) : false,
    alwaysShow: false
  };
}

async function submitMenu(data: Api.SystemManage.SaveMenuParams) {
  if (isEdit.value && props.rowData) {
    const { scopeType: _scopeType, objectType: _objectType, ...updateData } = data;

    return fetchUpdateMenu({ id: props.rowData.id, ...updateData });
  }

  return fetchCreateMenu(data);
}

async function initModel() {
  initializingModel.value = true;
  model.value = createDefaultModel();

  if (isAddChild.value && props.rowData) {
    model.value.parentId = props.rowData.id;

    if (isObjectScope.value && props.rowData.type === 2) {
      model.value.type = 3;
    }
  }

  if (!isEdit.value || !props.rowData) {
    applyMenuTypePreset(model.value.type);
    syncCurrentRouteFields();

    await nextTick();
    clearFormValidation();
    initializingModel.value = false;
    return;
  }

  detailLoading.value = true;

  const { error, data } = await fetchGetMenu(props.rowData.id);

  detailLoading.value = false;

  if (!error) {
    model.value = mapMenuDetailToModel(data);
    applyMenuTypePreset(model.value.type);
    syncCurrentRouteFields();
  }

  await nextTick();
  clearFormValidation();
  initializingModel.value = false;
}

async function handleSubmit() {
  await validate();

  submitting.value = true;

  const submitData = getSubmitData();
  const { error } = await submitMenu(submitData);

  submitting.value = false;

  if (error) {
    return;
  }

  window.$message?.success($t(isEdit.value ? 'common.updateSuccess' : 'common.addSuccess'));

  closeModal();
  emit('submitted');
}

watch(
  () => model.value.type,
  value => {
    applyMenuTypePreset(value);
  }
);

watch(
  () => model.value.routeKind,
  async (value, oldValue) => {
    if (initializingModel.value) {
      return;
    }

    resetRouteKindFields(value, oldValue);
    syncCurrentRouteFields();
    await nextTick();
    clearFormValidation();
  }
);

watch(
  () => model.value.parentId,
  async () => {
    syncCurrentRouteFields();
    await nextTick();
    clearFormValidation();
  }
);

watch(
  () => model.value.path,
  () => {
    if (isDirectoryRoute.value || isIframeRoute.value || isExternalRoute.value || isRedirectRoute.value) {
      syncCurrentRouteFields();
    }
  }
);

watch(
  () => model.value.pageResourcePath,
  value => {
    if (!isViewRoute.value || !value) {
      return;
    }

    const routeBinding = routeBindingMap.value.get(value);

    if (!routeBinding) {
      return;
    }

    if (!initializingModel.value) {
      applyRouteBindingMeta(routeBinding);
    }

    syncViewRouteFields();
  }
);

watch(
  () => model.value.iframeUrl,
  () => {
    if (isIframeRoute.value) {
      syncIframeRouteFields();
    }
  }
);

watch(
  () => model.value.externalUrl,
  () => {
    if (isExternalRoute.value) {
      syncExternalRouteFields();
    }
  }
);

watch(
  () => model.value.redirectTarget,
  () => {
    if (isRedirectRoute.value) {
      syncRedirectRouteFields();
    }
  }
);

watch(visible, value => {
  if (value) {
    initModel();
  }
});
</script>

<template>
  <BusinessFormDialog
    v-model="visible"
    :title="title"
    preset="lg"
    :loading="detailLoading"
    :confirm-loading="submitting"
    @confirm="handleSubmit"
  >
    <ElForm ref="formRef" :model="model" :rules="rules" label-position="top">
      <BusinessFormSection :title="$t('page.system.menu.sections.basic')">
        <ElRow :gutter="16">
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.menu.menuType')" prop="type">
              <ElRadioGroup v-model="model.type" class="business-form-radio-group" :disabled="isEdit">
                <ElRadio
                  v-for="{ label, value, disabled } in menuTypeRadioOptions"
                  :key="value"
                  :value="value"
                  :disabled="disabled"
                >
                  {{ $t(label) }}
                </ElRadio>
              </ElRadioGroup>
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.menu.parentId')" prop="parentId">
              <ElTreeSelect
                v-model="model.parentId"
                class="w-full"
                node-key="value"
                check-strictly
                filterable
                default-expand-all
                :data="parentTreeOptions"
                :filter-node-method="filterParentTreeNode"
                :render-after-expand="false"
                :placeholder="$t('page.system.menu.form.parentId')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.menu.menuName')" prop="name">
              <ElInput v-model="model.name" :placeholder="$t('page.system.menu.form.menuName')" />
            </ElFormItem>
          </ElCol>
          <ElCol v-if="showPermissionField" :span="12">
            <ElFormItem :label="$t('page.system.menu.permission')" prop="permission">
              <ElInput v-model="model.permission" :placeholder="$t('page.system.menu.form.permission')" />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.menu.order')" prop="sort">
              <ElInputNumber
                v-model="model.sort"
                class="w-full"
                :min="0"
                :placeholder="$t('page.system.menu.form.sort')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.menu.menuStatus')" prop="status">
              <ElRadioGroup v-model="model.status" class="business-form-radio-group">
                <ElRadio v-for="{ label, value } in commonStatusOptions" :key="value" :value="value">
                  {{ $t(label) }}
                </ElRadio>
              </ElRadioGroup>
            </ElFormItem>
          </ElCol>
        </ElRow>
      </BusinessFormSection>

      <BusinessFormSection v-if="showRouteSection" :title="$t('page.system.menu.sections.route')">
        <ElRow :gutter="16">
          <ElCol v-if="showRouteKindField" :span="12">
            <ElFormItem prop="routeKind">
              <template #label>
                <span class="business-form-label-with-tip">
                  <span>{{ $t('page.system.menu.routeKind') }}</span>
                  <ElPopover
                    popper-class="business-form-label-popover"
                    placement="top-start"
                    :width="320"
                    trigger="hover"
                  >
                    <template #default>
                      <div class="business-form-label-popover__title">
                        {{ $t('page.system.menu.tips.routeKind') }}
                      </div>
                      <div class="business-form-label-popover__summary">
                        {{ $t('page.system.menu.tips.routeKindSummary') }}
                      </div>
                      <div class="business-form-label-popover__list">
                        <div
                          v-for="item in routeKindTipItems"
                          :key="item.value"
                          class="business-form-label-popover__item"
                        >
                          <div class="business-form-label-popover__item-title">
                            {{ item.label }}
                          </div>
                          <div class="business-form-label-popover__item-desc">
                            {{ item.description }}
                          </div>
                        </div>
                      </div>
                    </template>
                    <template #reference>
                      <span class="business-form-label-tip">
                        <icon-fe:question />
                      </span>
                    </template>
                  </ElPopover>
                </span>
              </template>
              <ElSelect v-model="model.routeKind" :placeholder="$t('page.system.menu.form.routeKind')">
                <ElOption
                  v-for="{ label, value } in routeKindSelectOptions"
                  :key="value"
                  :label="$t(label)"
                  :value="value"
                />
              </ElSelect>
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showPageResourceField" :span="12">
            <ElFormItem prop="pageResourcePath">
              <template #label>
                <span class="business-form-label-with-tip">
                  <span>{{ routeBindingFieldLabel }}</span>
                  <ElTooltip
                    :content="routeBindingFieldTip"
                    popper-class="business-form-label-tooltip"
                    placement="top-start"
                  >
                    <span class="business-form-label-tip">
                      <icon-fe:question />
                    </span>
                  </ElTooltip>
                </span>
              </template>
              <ElSelect v-model="model.pageResourcePath" filterable :placeholder="routeBindingFieldPlaceholder">
                <ElOption v-for="{ label, value } in routeBindingOptions" :key="value" :label="label" :value="value" />
              </ElSelect>
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showReadonlyPathField || showEditablePathField" :span="12">
            <ElFormItem prop="path">
              <template #label>
                <span class="business-form-label-with-tip">
                  <span>{{ $t('page.system.menu.routePath') }}</span>
                  <ElTooltip
                    :content="$t('page.system.menu.tips.routePath')"
                    popper-class="business-form-label-tooltip"
                    placement="top-start"
                  >
                    <span class="business-form-label-tip">
                      <icon-fe:question />
                    </span>
                  </ElTooltip>
                </span>
              </template>
              <ElInput
                v-if="showReadonlyPathField"
                :model-value="displayRoutePath"
                readonly
                :placeholder="$t('page.system.menu.form.path')"
              />
              <ElInput v-else v-model="model.path" :placeholder="$t('page.system.menu.form.path')" />
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showIconField" :span="12">
            <ElFormItem :label="$t('page.system.menu.icon')" prop="icon">
              <div class="flex items-center gap-8px">
                <div class="min-w-0 flex-1">
                  <CustomIconSelect v-model:value="iconFieldValue" :icon-groups="menuIconGroups" />
                </div>
                <ElButton @click="model.icon = ''">
                  {{ $t('common.reset') }}
                </ElButton>
              </div>
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showIframeUrlField" :span="12">
            <ElFormItem :label="$t('page.system.menu.iframeUrl')" prop="iframeUrl">
              <ElInput v-model="model.iframeUrl" :placeholder="$t('page.system.menu.form.iframeUrl')" />
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showExternalUrlField" :span="12">
            <ElFormItem :label="$t('page.system.menu.externalUrl')" prop="externalUrl">
              <ElInput v-model="model.externalUrl" :placeholder="$t('page.system.menu.form.externalUrl')" />
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showRedirectTargetField" :span="12">
            <ElFormItem :label="$t('page.system.menu.redirectTarget')" prop="redirectTarget">
              <ElInput v-model="model.redirectTarget" :placeholder="$t('page.system.menu.form.redirectTarget')" />
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showReadonlyComponentField || showManualComponentField" :span="12">
            <ElFormItem prop="component">
              <template #label>
                <span class="business-form-label-with-tip">
                  <span>{{ $t('page.system.menu.component') }}</span>
                  <ElTooltip
                    :content="$t('page.system.menu.tips.component')"
                    popper-class="business-form-label-tooltip"
                    placement="top-start"
                  >
                    <span class="business-form-label-tip">
                      <icon-fe:question />
                    </span>
                  </ElTooltip>
                </span>
              </template>
              <ElInput
                v-if="showReadonlyComponentField"
                :model-value="model.component"
                readonly
                :placeholder="$t('page.system.menu.form.component')"
              />
              <ElInput v-else v-model="model.component" :placeholder="$t('page.system.menu.form.component')" />
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showReadonlyComponentNameField || showManualComponentNameField" :span="12">
            <ElFormItem :label="$t('page.system.menu.componentName')" prop="componentName">
              <ElInput
                v-if="showReadonlyComponentNameField"
                :model-value="model.componentName"
                readonly
                :placeholder="$t('page.system.menu.form.componentName')"
              />
              <ElInput v-else v-model="model.componentName" :placeholder="$t('page.system.menu.form.componentName')" />
            </ElFormItem>
          </ElCol>

          <ElCol v-if="showReadonlyRouteProps || showRoutePropsEditor" :span="24">
            <ElFormItem :label="$t('page.system.menu.routePropsJson')" prop="routePropsJson">
              <ElInput
                v-if="showReadonlyRouteProps"
                :model-value="model.routePropsJson"
                readonly
                type="textarea"
                :rows="4"
                :placeholder="$t('page.system.menu.form.routePropsJsonHint')"
              />
              <ElInput
                v-else
                v-model="model.routePropsJson"
                type="textarea"
                :rows="4"
                :placeholder="$t('page.system.menu.form.routePropsJsonHint')"
              />
            </ElFormItem>
          </ElCol>
        </ElRow>
      </BusinessFormSection>

      <BusinessFormSection v-if="showDisplaySection" :title="$t('page.system.menu.sections.display')">
        <ElRow :gutter="16">
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.menu.keepAlive')" prop="keepAlive">
              <div class="business-form-switch-field">
                <ElSwitch v-model="keepAliveSwitch" />
              </div>
            </ElFormItem>
          </ElCol>
        </ElRow>
      </BusinessFormSection>
    </ElForm>
  </BusinessFormDialog>
</template>

<style scoped></style>
