<script setup lang="ts">
import { Notify, Toast } from '@/platform';

import {
  type FieldKey,
  getCaptchaMethod,
  processMethod,
  registerAccountMethod,
  registerFieldsMethod,
  scanAccountMethod, searchAccountMethod,
} from '@/request/api';

import { useVisitor } from '@/stores';
import { useRouteQuery } from '@vueuse/router';

import { useCaptcha, useForm, useRequest, useWatcher } from 'alova/client';

import type { FieldRule, FormInstance, NumberKeyboardProps, PasswordInputProps } from 'vant';
import { RadioGroup as vanRadioGroup }                                           from 'vant';

import NavHomeSelect from '@/assets/images/nav-home.select.png?url';

interface Field {
  control: {
    label: string; placeholder?: string;
    type?: string; min?: number; max?: number; minlength?: number; maxlength?: number;
    clearable?: boolean; border?: boolean; readonly?:boolean;
  };
  component?: |
    { name: 'radio', options: { label: string; value: string; }[] } |
    { name: 'code', props?: Partial<PasswordInputProps> };
  keyboard?: { show: boolean; } & Partial<NumberKeyboardProps>;
  suffix?: string;
  rules?: FieldRule[];
}

const Fields: Record<FieldKey, Field> = {
  height: {
    control: {
      label: '身高', placeholder: '请输入身高',
      type: 'number', min: 1, max: 300, clearable: true, readonly: true,
      maxlength: 5,
    },
    keyboard: { show: false, title: '身高', extraKey:'.', closeButtonText: '完成' },
    suffix: 'cm',
  },
  weight: {
    control: {
      label: '体重', placeholder: '请输入体重',
      type: 'number', min: 1, max: 300, clearable: true, readonly: true,
      maxlength: 5,
    },
    keyboard: { show: false, title: '体重', extraKey:'.', closeButtonText: '完成' },
    suffix: 'kg',
  },
  age: {
    control: {
      label: '年龄', placeholder: '请输入年龄',
      type: 'digit', min: 0, max: 300, clearable: true, readonly: true,
      maxlength: 3,
    },
    keyboard: { show: false, title: '年龄', closeButtonText: '完成' },
    suffix: '岁',
  },
  sex: {
    control: { label: '性别', border: false },
    component: {
      name: 'radio' as const,
      options: [
        { label: '男', value: '0' },
        { label: '女', value: '1' },
      ],
    },
  },
  isEasyAllergy: {
    control: { label: '容易过敏', border: false },
    component: {
      name: 'radio' as const,
      options: [
        { label: '是', value: 'Y' },
        { label: '否', value: 'N' },
      ],
    },
  },
  name: {
    control: {
      label: '姓名', placeholder: '请输入姓名',
      type: 'text', maxlength: 10, clearable: true,
    },
  },
  cardno: {
    control: {
      label: '身份证号', placeholder: '请输入身份证号',
      type: 'text', maxlength: 18, minlength: 18, clearable: true, readonly: true,
    },
    keyboard: { show: false, title: '身份证号', extraKey: 'X', closeButtonText: '完成' },
    rules: [
      { required: true, message: '请输入身份证号' },
      {
        validator: (value: string) => value && value.length === 18,
        message: '请输入正确的身份证',
        trigger: 'onBlur',
      },
    ],
  },
  phone: {
    control: {
      label: '手机号码', placeholder: '请输入手机号码',
      type: 'tel', maxlength: 11, minlength: 11, clearable: true, readonly: true,
    },
    keyboard: { show: false, title: '手机号码', closeButtonText: '完成' },
    rules: [
      { required: true, message: '请输入手机号码' },
      {
        validator: (value: string) => value && value.length === 11,
        message: '请输入正确的手机号码',
        trigger: 'onBlur',
      },
    ],
  },
  code: {
    control: {
      label: '验证码', placeholder: '请输入验证码',
      type: 'digit', maxlength: 6, minlength: 6, clearable: true,
      border: false,
    },
    component: {
      name: 'code' as const,
      props: { mask: false },
    },
    keyboard: { show: false, title: '验证码', closeButtonText: '完成' },
    rules: [
      { required: true, message: '请输入验证码' },
      {
        validator: (value: string) => value && value.length === 6,
        message: '请输入验证码',
        trigger: [ 'onChange', 'onBlur' ],
      },
    ],
  },
};


const fields = ref<( Field & { name: FieldKey } )[]>([]);
const { loading } = useRequest(registerFieldsMethod).onSuccess(({ data }) => {
  fields.value = data.map(name => {return { ...Fields[ name ], name };});
});

const Visitor = useVisitor();
const formRef = useTemplateRef<FormInstance>('register-form');
const { form: modelRef, loading: submitting, send: submit } = useForm(data => registerAccountMethod(data), {
  initialForm: { code: '' } as Record<string, any>,
}).onSuccess(async ({ data }) => {
  Visitor.patientId = data;
  Toast.success(`操作成功`);
  try {
    submitting.value = true;
    await handle();
  } finally {
    submitting.value = false;
  }
});

const { loading: searching, send: search } = useRequest((data) => searchAccountMethod(data), {
  immediate: false,
}).onSuccess(({ data }) => {
  modelRef.value = { ...modelRef.value, ...data };
});

let captchaLoaded = false;
const { loading: captchaLoading, countdown, send: getCaptcha } = useCaptcha(
  () => getCaptchaMethod(modelRef.value.phone!),
  { initialCountdown: 60 },
).onSuccess(({ data }) => {
  captchaLoaded = true;
  Toast.success(data ?? '获取成功')
});
const getCaptchaHandle = async () => {
  try {
    await formRef.value?.validate('phone');
    await getCaptcha();
    const field = fields.value.find(field => field.name === 'code');
    if ( field?.keyboard ) { field.keyboard.show = true; }
  } catch ( e: any ) {
    Toast.warning(e?.message);
  }
};

const searchHandle = async (key: 'cardno' | 'code') => {
  try {
    await formRef.value?.validate(key);
    await search(modelRef.value).catch();
  } catch ( e: any ) {
    Toast.warning(e?.message);
  }
};

function onKeyboardBlur(field: Field & { name: FieldKey }) {
  if ( field?.name === 'phone' && !captchaLoaded ) { getCaptchaHandle(); }
  if ( field?.name === 'cardno' ) { searchHandle('cardno'); }
  if ( field?.name === 'code' ) { searchHandle('code'); }
}

function onSubmitHandle() {
  submit(toValue(modelRef));
}

const router = useRouter();
const { send: handle } = useRequest(
  () => processMethod('/register'),
  { immediate: false },
).onSuccess(
  ({ data }) => {
    if ( data ) {
      router.replace(data);
    } else {
      Notify.warning(`[路由] 配置异常无法解析正确路径，请联系管理员`);
    }
  });

function fix(key: FieldKey) {
  for ( const field of fields.value ) {
    if (field.keyboard?.show && field.name !== key ) field.keyboard.show = false;
  }
}

const scan = useRouteQuery<string>('scan');
useWatcher(
  () => scanAccountMethod(sessionStorage.getItem(`scan_${ scan.value }`)!),
  [ scan ],
  {
    immediate: true,
    async middleware(_, next) {
      if ( scan.value ) {
        let scanToastRef: any;
        try {
          scanToastRef = Toast.loading(100, { message: '加载中' });
          await next();
        } catch ( error ) {} finally {
          scanToastRef?.close?.();
        }
      }
    },
  },
).onSuccess(({ data }) => {
  Toast.success('扫码成功');
  modelRef.value = { ...modelRef.value, ...data };
});

onBeforeUnmount(() => {
  for ( let i = 0; i < sessionStorage.length; i++ ) {
    const key = sessionStorage.key(i);
    if ( key?.startsWith('scan_') ) sessionStorage.removeItem(key);
  }
});
</script>
<template>
  <div>
    <div class="page-header flex py-4 px-4">
      <div class="grow shrink-0 h-full min-w-16"></div>
      <div class="grow-[3] shrink mx-2 flex flex-col justify-center overflow-hidden">
        <div class="font-bold text-3xl text-nowrap text-center tracking-wide overflow-ellipsis overflow-hidden">
          建档
        </div>
      </div>
      <div class="grow shrink-0 h-full min-w-16 flex items-center justify-end overflow-hidden">
        <router-link :to="{ path: '/screen' }" replace>
          <img class="size-8 object-scale-down" :src="NavHomeSelect" alt="返回首页">
        </router-link>
      </div>
    </div>
    <div class="page-content p-6">
      <van-form class="register-form" ref="register-form" colon required="auto"
                scroll-to-error scroll-to-error-position="center"
                @submit="onSubmitHandle()"
      >
        <van-cell-group :border="false">
          <template v-for="field in fields" :key="field.name">
            <van-field v-model="modelRef[field.name]" :name="field.name" :id="field.name"
                       :rules="field.rules" v-bind="field.control"
                       :class="{'no-border': field.control?.border === false}"
                       :focused="field.keyboard?.show" @focus="field.keyboard && (field.keyboard.show = true)"
                       @blur="field.keyboard && (field.keyboard.show = false)"
                       :readonly="field.control.readonly" @click="field.keyboard && (field.keyboard.show = true)"
            >
              <template #input v-if="field.component?.name === 'radio'">
                <van-radio-group v-model="modelRef[field.name]" direction="horizontal" shape="dot">
                  <van-radio v-for="option in field.component?.options" :key="option.value" :name="option.value">
                    {{ option.label }}
                  </van-radio>
                </van-radio-group>
              </template>
              <template #input v-else-if="field.component?.name === 'code'">
                <van-password-input
                  style="width: 100%;"
                  v-model:value="modelRef[field.name]" v-bind="(field.component as any)!.props"
                  :focused="field.keyboard?.show" @focus="field.keyboard && (field.keyboard.show = true);fix('code')"
                />
              </template>
              <template #button>
                <div class="text-primary cursor-pointer">
                  <template v-if="field.component?.name === 'code'">
                    <div class="text-primary cursor-pointer" @click="getCaptchaHandle()">
                      {{ captchaLoading ? '发送中...' : countdown > 0 ? `${ countdown }后可重发` : '获取验证码' }}
                    </div>
                  </template>
                  <template v-else>{{ field.suffix }}</template>
                </div>
              </template>
            </van-field>
            <van-number-keyboard
              v-if="field.keyboard"
              v-model="modelRef[field.name]"
              v-bind="field.keyboard" :maxlength="field.control.maxlength"
              @blur="field.keyboard.show = false; onKeyboardBlur(field)"
            />
          </template>
        </van-cell-group>
      </van-form>
      <div class="m-4">
        <div class="m-auto size-16 cursor-pointer">
          <van-loading v-if="submitting" type="spinner" size="64" color="#38ff6e" />
          <img v-else class="size-full"
               src="@/assets/images/next-step.svg" alt="提交" @click="formRef?.submit()"
          >
        </div>
      </div>
    </div>
  </div>
</template>
<style scoped lang="scss">
.register-form {
  .van-field {
    margin: 0;
    padding: 0;
  }

  .van-field.no-border {
    :deep(.van-field__control) {
      padding: 0;
      border: none;
      border-radius: 8px;
      text-align: center;
    }
  }

  :deep(.van-field__label) {
    margin-bottom: 24px;
    padding: 8px 0;
    min-width: 100px;
    font-size: 18px;
  }

  :deep(.van-field__control) {
    margin-bottom: 24px;
    padding: 8px;
    border: 1px solid #38ff6e;
    border-radius: 8px;
    text-align: center;
  }

  :deep(.van-field__clear) {
    align-self: flex-start;
    display: flex;
    align-items: center;
    height: 42px;
  }

  :deep(.van-field__button) {
    margin-bottom: 24px;
    padding: 8px var(--van-padding-xs);
    min-width: 100px;
    font-size: 18px;
    text-align: left;
  }

  :deep(.van-field__error-message) {
    position: absolute;
    top: 40px + 2px;
  }

  :deep(.van-password-input) {
    margin: 0;
  }

  :deep(.van-password-input__security) {
    justify-content: space-between;
    align-items: center;
    text-align: center;
    $size: 40px;
    height: $size + 2px;

    &::after {
      display: none;
    }

    li {
      height: $size;
      width: $size;
      flex: none;
      border: 1px solid #38ff6e;
      border-radius: 8px;
    }
  }
}

.van-radio-group {
  height: 40px + 2px;
}
</style>
