<script setup lang="ts">
import { computed, nextTick, ref, watch } from 'vue';
import { userGenderOptions } from '@/constants/business';
import {
  fetchAssignUserRoles,
  fetchCreateUser,
  fetchGetUser,
  fetchGetUserRoleIds,
  fetchUpdateUser
} from '@/service/api';
import { useForm, useFormRules } from '@/hooks/common/form';
import { translateOptions } from '@/utils/common';
import BusinessFormDialog from '@/components/custom/business-form-dialog.vue';
import BusinessFormSection from '@/components/custom/business-form-section.vue';
import { $t } from '@/locales';

defineOptions({ name: 'UserOperateDialog' });

interface Props {
  operateType: UI.TableOperateType;
  userId?: number | null;
  currentDeptId?: number | null;
  deptTree: Api.SystemManage.Dept[];
  postOptions: Api.SystemManage.PostSimple[];
  roleOptions: Api.SystemManage.RoleSimple[];
}

const props = defineProps<Props>();

interface Emits {
  (e: 'submitted', userId?: number): void;
}

const emit = defineEmits<Emits>();

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

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

const loading = ref(false);
const submitting = ref(false);

const title = computed(() => {
  const titles: Record<UI.TableOperateType, string> = {
    add: $t('page.system.user.addUser'),
    edit: $t('page.system.user.editUser')
  };

  return titles[props.operateType];
});

const isEdit = computed(() => props.operateType === 'edit');

type Model = Api.SystemManage.SaveUserParams & {
  roleIds: number[];
};

const model = ref<Model>(createDefaultModel());

const genderOptions = computed(() =>
  translateOptions(userGenderOptions).map(item => ({
    ...item,
    value: Number(item.value) as Api.SystemManage.UserGender
  }))
);

const deptTreeProps = {
  value: 'id',
  label: 'name',
  children: 'children'
} as const;

function createDefaultModel(): Model {
  return {
    username: '',
    nickname: '',
    remark: '',
    deptId: props.currentDeptId ?? 0,
    positionId: null,
    email: '',
    mobile: '',
    sex: 1,
    avatar: '',
    password: '',
    roleIds: []
  };
}

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

const rules = computed(() => {
  const passwordRules = isEdit.value
    ? []
    : [createRequiredRule($t('page.system.user.form.password')), patternRules.pwd];

  return {
    username: [createRequiredRule($t('page.system.user.form.userName')), patternRules.userName],
    deptId: [createRequiredRule($t('page.system.user.form.deptName'))],
    positionId: [createRequiredRule($t('page.system.user.form.positionName'))],
    mobile: getNullableText(model.value.mobile) ? [patternRules.phone] : [],
    email: getNullableText(model.value.email) ? [patternRules.email] : [],
    password: passwordRules
  } satisfies Record<string, App.Global.FormRule[]>;
});

async function handleInitModel() {
  model.value = createDefaultModel();

  if (!isEdit.value || !props.userId) {
    return;
  }

  loading.value = true;

  const [userResult, roleResult] = await Promise.all([fetchGetUser(props.userId), fetchGetUserRoleIds(props.userId)]);

  loading.value = false;

  if (userResult.error) {
    return;
  }

  const user = userResult.data;

  model.value = {
    username: user.username,
    nickname: user.nickname ?? '',
    remark: user.remark ?? '',
    deptId: user.deptId,
    positionId: user.positionId ?? null,
    email: user.email ?? '',
    mobile: user.mobile ?? '',
    sex: user.sex ?? 0,
    avatar: user.avatar ?? '',
    password: '',
    roleIds: roleResult.error ? [] : roleResult.data
  };
}

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

async function handleSubmit() {
  await validate();

  const payload: Api.SystemManage.SaveUserParams = {
    username: model.value.username.trim(),
    nickname: getNullableText(model.value.nickname),
    remark: getNullableText(model.value.remark),
    deptId: model.value.deptId,
    positionId: model.value.positionId,
    email: getNullableText(model.value.email),
    mobile: getNullableText(model.value.mobile),
    sex: model.value.sex,
    avatar: getNullableText(model.value.avatar)
  };

  if (!isEdit.value) {
    payload.password = String(model.value.password ?? '').trim();
  }

  submitting.value = true;

  let userId = props.userId ?? undefined;

  if (isEdit.value && props.userId) {
    const result = await fetchUpdateUser({ id: props.userId, ...payload });

    if (result.error) {
      submitting.value = false;
      return;
    }
  } else {
    const result = await fetchCreateUser(payload);

    if (result.error) {
      submitting.value = false;
      return;
    }

    userId = result.data;
  }

  if (userId !== undefined) {
    const roleResult = await fetchAssignUserRoles({
      userId,
      roleIds: model.value.roleIds
    });

    if (roleResult.error) {
      submitting.value = false;
      return;
    }
  }

  submitting.value = false;

  window.$message?.success(isEdit.value ? $t('common.updateSuccess') : $t('common.addSuccess'));
  closeDialog();
  emit('submitted', userId);
}

watch(visible, async value => {
  if (!value) {
    return;
  }

  await handleInitModel();
  await nextTick();
  formRef.value?.clearValidate();
});
</script>

<template>
  <BusinessFormDialog
    v-model="visible"
    :title="title"
    preset="lg"
    :loading="loading"
    :confirm-loading="submitting"
    max-body-height="70vh"
    @confirm="handleSubmit"
  >
    <ElForm ref="formRef" :model="model" :rules="rules" label-position="top" autocomplete="off">
      <input class="business-form-autofill-guard" type="text" name="fake-username" autocomplete="username" />
      <input class="business-form-autofill-guard" type="password" name="fake-password" autocomplete="new-password" />

      <BusinessFormSection :title="$t('page.system.user.sections.basicInfo')">
        <ElRow :gutter="16">
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.userName')" prop="username">
              <ElInput
                v-model="model.username"
                name="system-user-username"
                autocomplete="off"
                :placeholder="$t('page.system.user.form.userName')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.nickName')" prop="nickname">
              <ElInput
                v-model="model.nickname"
                name="system-user-nickname"
                autocomplete="off"
                :placeholder="$t('page.system.user.form.nickName')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol v-if="!isEdit" :span="12">
            <ElFormItem :label="$t('page.system.user.password')" prop="password">
              <ElInput
                v-model="model.password"
                name="system-user-password"
                show-password
                autocomplete="new-password"
                :placeholder="$t('page.system.user.form.password')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.userGender')" prop="sex">
              <ElSelect v-model="model.sex" :placeholder="$t('page.system.user.form.userGender')">
                <ElOption v-for="{ label, value } in genderOptions" :key="value" :label="label" :value="value" />
              </ElSelect>
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.userPhone')" prop="mobile">
              <ElInput
                v-model="model.mobile"
                name="system-user-mobile"
                autocomplete="off"
                :placeholder="$t('page.system.user.form.userPhone')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.userEmail')" prop="email">
              <ElInput
                v-model="model.email"
                name="system-user-email"
                autocomplete="off"
                :placeholder="$t('page.system.user.form.userEmail')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="24">
            <ElFormItem :label="$t('page.system.user.remark')" prop="remark">
              <ElInput
                v-model="model.remark"
                type="textarea"
                :rows="3"
                :placeholder="$t('page.system.user.form.remark')"
              />
            </ElFormItem>
          </ElCol>
        </ElRow>
      </BusinessFormSection>

      <BusinessFormSection :title="$t('page.system.user.sections.organizationInfo')">
        <ElRow :gutter="16">
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.deptName')" prop="deptId">
              <ElTreeSelect
                v-model="model.deptId"
                check-strictly
                clearable
                default-expand-all
                :data="deptTree"
                :props="deptTreeProps"
                :render-after-expand="false"
                :placeholder="$t('page.system.user.form.deptName')"
              />
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.positionName')" prop="positionId">
              <ElSelect v-model="model.positionId" clearable :placeholder="$t('page.system.user.form.positionName')">
                <ElOption v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
              </ElSelect>
            </ElFormItem>
          </ElCol>
          <ElCol :span="12">
            <ElFormItem :label="$t('page.system.user.userRole')" prop="roleIds">
              <ElSelect
                v-model="model.roleIds"
                multiple
                collapse-tags
                collapse-tags-tooltip
                :placeholder="$t('page.system.user.form.userRole')"
              >
                <ElOption v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
              </ElSelect>
            </ElFormItem>
          </ElCol>
        </ElRow>
      </BusinessFormSection>
    </ElForm>
  </BusinessFormDialog>
</template>

<style scoped>
.business-form-autofill-guard {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
</style>
