<script setup lang="ts">
import RemoteSelect from '@/libs/v-select-page/RemoteSelect.vue';

import type { ReportSchemeItemModel } from '@/model';

import {
  acupointsMethod,
  herbalMedicineUnitMethod,
  medicinesMethod,
  schemeCategoryOptionsMethod,
} from '@/request/api/dictionary.api';

import { editSchemeMethod, searchSchemeMethod } from '@/request/api/report.api';

import { withResolvers }                     from '@/tools/promise';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons-vue';
import { useRequest, useWatcher }            from 'alova/client';

import { Form, message as Message, notification as Notification } from 'ant-design-vue';
import type { Rule }                                              from 'ant-design-vue/es/form';

import { h } from 'vue';


const getModel = (item: Partial<ReportSchemeItemModel> = {}): ReportSchemeItemModel => {
  model.id = item.id;
  model.name = item.name;
  model.category = item.category;
  model.type = item.type;
  model.content = item.content?.map(_ => Object.assign({}, _)) ?? [];
  model.descriptions = item.descriptions?.map(_ => Object.assign({}, _)) ?? [];
};

const dataValidValidator = (_rule, value: number, callback): Rule['validator'] => {
  return model.content?.filter(t => t.name)?.length || model.descriptions?.filter(t => t?.name)?.length
         ? Promise.resolve()
         : Promise.reject();
};

const groupValidatorDescription = (_rule, value: Record<string, string>, callback): Rule['validator'] => {
  if ( value ) return Promise.resolve();

  const index = _rule.field.split('.')[ 1 ];
  const values = [ model.descriptions[ index ]?.name, model.descriptions[ index ]?.description ];
  return values.some(v => !!v) ? Promise.reject() : Promise.resolve();
};
const groupValidatorContent = (_rule, value: Record<string, string>, callback): Rule['validator'] => {
  if ( value ) return Promise.resolve();

  const index = _rule.field.split('.')[ 1 ];
  const values = [ model.content[ index ]?.name, model.content[ index ]?.doase ];
  return values.some(v => !!v) ? Promise.reject() : Promise.resolve();
};

const props = defineProps<{
  value?: ReportSchemeItemModel,
  reportId?: string;
}>();
const emits = defineEmits<{
  'update:value': [ model: ReportSchemeItemModel ],
  'destroy': [],
}>();
const model = reactive<ReportSchemeItemModel>({
  count: 0,
});
const rules = reactive<Record<string, Rule[]>>({
  category: [ { required: true, message: '请选择方案类型' } ],
  count: [],
});

const nextDestroy = ref(false);
const { validateInfos } = Form.useForm(model, rules);

const { data: medicineUnitOptions, loading: medicineUnitLoading } = useRequest(
  herbalMedicineUnitMethod,
  { initialData: [] },
);
const { data: categoryOptions, loading: categoryLoading } = useRequest(
  schemeCategoryOptionsMethod,
  { initialData: [] },
).onSuccess(({ data }) => {
  if ( !model.category ) model.category = data[ 0 ]?.value;
});

const { data: scheme, loading: schemeLoading, send: search } = useWatcher(
  (keyword) => searchSchemeMethod(keyword, model),
  [ () => model.category ],
  {
    middleware: (_, next) => { if ( model.category ) next(); },
  },
);

const { send: submit } = useRequest(
  () => editSchemeMethod(props.reportId, model),
  { immediate: false },
).onSuccess(({ data }) => {
  Notification.success({
    message: '操作成功',
  });
  emits('update:value', data);
  if ( nextDestroy.value ) emits('destroy');
});


const editableContent = computed(() => [ 'acupoint', 'medicine' ].includes(model.type));

const appendContent = (data?: Record<string, any>) => {
  if ( !editableContent.value ) return;
  model.content?.push({ id: `custom-${ Date.now() }`, ...data });
};

const removeContent = (data: Record<string, any>, index?: number) => {
  if ( !editableContent.value ) return;
  index ??= model.content?.findIndex(t => t.id === data.id);
  if ( index != null && index > -1 ) model.content?.splice(index, 1);
  if ( model.content?.length === 0 ) appendContent();
};

const updateContent = (data: Record<string, any> | null, index: number) => {
  const old = model.content[ index ];
  const name = data?.name;
  const doase = data?.doase ?? old?.doase;
  const imgUrl = data?.photo;

  let unit = data?.unit ?? old?.unit;
  if ( model.type === 'medicine' ) unit ??= medicineUnitOptions.value[ 0 ]?.value;

  model.content[ index ] = { ...model.content[ index ], name, imgUrl, doase, unit, type: model.type };
};

const appendDescription = (data?: Record<string, any>) => {
  model.descriptions?.push({ id: `custom-${ Date.now() }`, name: '', description: '', ...data });
};

const removeDescription = (data: Record<string, any>, index?: number) => {
  index ??= model.descriptions?.findIndex(t => t.id === data.id);
  if ( index != null && index > -1 ) model.descriptions?.splice(index, 1);
  if ( model.descriptions?.length === 0 ) appendDescription();
};


const toggleTypeConfirmProps = shallowReactive({
  show: false,
  title: '确定切换?',
  resolve: () => void 0,
});
const toggleTypeConfirm = (value: string | null) => {
  const { promise, resolve } = withResolvers<boolean>();
  setTimeout(() => document.documentElement.addEventListener('click', () => resolve(false), { once: true }), 20);
  toggleTypeConfirmProps.resolve = resolve;
  toggleTypeConfirmProps.title = value ? `确定切换数据` : `确定取消数据`;
  toggleTypeConfirmProps.show = true;
  promise.then(() => toggleTypeConfirmProps.show = false);
  return promise;
};
const toggleType = async (value) => {
  let toggle = !model.content?.some(t => t.name) || await toggleTypeConfirm(value);
  if ( toggle ) {
    model.type = value;
    model.content = [];
    appendContent();
  }
};

onBeforeMount(() => {
  getModel(props.value);
  if ( !model.content?.length ) appendContent();
  if ( !model.descriptions?.length ) appendDescription();
});

function onFinishFailed(e) {
  Message.warning(`请补充完整！`);
}

function selectSchemeHandle(name: string) {
  const data = scheme.value.find((s) => s.label === name);
  Object.keys(data ?? {}).forEach((key: string) => {
    const value = data[ key ];
    if ( Array.isArray(value) ) {
      model[ key ] = [ ...value ];
    } else if ( value && typeof value === 'object' ) {
      model[ key ] = { ...value };
    } else {
      model[ key ] = value;
    }
  });
}
</script>
<template>
  <a-form
    class="form-wrapper" :model="model" scroll-to-first-error
    @finish="submit()" @finishFailed="onFinishFailed"
  >
    <a-form-item
      label="方案类型" name="category"
      :rules="{ required: true, message: '请选择方案类型' }"
    >
      <a-select v-model:value="model.category" placeholder="方案类型" showSearch
                :options="categoryOptions" :loading="categoryLoading"
      />
    </a-form-item>
    <a-form-item label="方案名称" v-bind="validateInfos.name">
      <a-select
        show-search placeholder="方案名称"
        :options="scheme" :loading="schemeLoading"
        :field-names="{label: 'label', value: 'label'}"
        v-model:value="model.name" @update:value="selectSchemeHandle"
        allow-clear @deselect="model.name = void 0;"
        @search="search" @dropdownVisibleChange="$event && search('');"
        not-found-content="请输入方案名称"
      ></a-select>
    </a-form-item>
    <a-form-item name="name">
      <template #label>
        <div>数据类型</div>
        <a-popconfirm :title="toggleTypeConfirmProps.title" description="确定后将重置【数据】"
                      placement="topLeft" :open="toggleTypeConfirmProps.show"
                      okText="确定" @confirm="()=>toggleTypeConfirmProps.resolve(true)"
                      cancelText="取消" @cancel="()=>toggleTypeConfirmProps.resolve(false)"
        />
      </template>
      <vxe-radio-group :modelValue="model.type" :strict="false" @update:modelValue="toggleType">
        <vxe-radio label="medicine" content="中药"></vxe-radio>
        <vxe-radio label="acupoint" content="穴位"></vxe-radio>
      </vxe-radio-group>
    </a-form-item>
    <!-- 可编辑数据 -->
    <template v-if="editableContent">
      <a-space-compact v-for="(row, index) in model.content" :key="row.id" block>
        <!-- 穴位 -->
        <template v-if="model.type === 'acupoint'">
          <a-form-item
            :label="index ? ' ' : '穴位数据'" :colon="!index"
            :name="['descriptions', index, 'name']" class="w-full"
          >
            <RemoteSelect
              :load="acupointsMethod" key-prop="name" v-model:value="row.name"
              @update="updateContent($event, index)"
            />
          </a-form-item>
        </template>
        <template v-else-if="model.type === 'medicine'">
          <a-form-item
            class="w-80%" :label="index ? ' ' : '中药数据'" :colon="!index"
            :name="['content', index, 'name']"
            :rules="{ validator: groupValidatorContent, message: '请选择中药' }"
          >
            <RemoteSelect
              :load="medicinesMethod" key-prop="name" v-model:value="row.name"
              @update="updateContent($event, index)"
            />
          </a-form-item>
          <a-form-item
            :name="['content', index, 'doase']"
            :rules="{ validator: groupValidatorContent, message: '请输入剂量' }"
          >
            <a-input-number v-model:value="row.doase" :min="0" :max="100" :precision="2" placeholder="剂量" />
          </a-form-item>
          <a-form-item
            class="w-120px"
            :name="['content', index, 'unit']"
            :rules="{ validator: groupValidatorContent, message: '请选择单位' }"
          >
            <a-select v-model:value="row.unit" placeholder="单位" :options="medicineUnitOptions"
                      :loading="medicineUnitLoading" allow-clear
            ></a-select>
          </a-form-item>
        </template>
        <span class="m-l-8px">
        <span class="p-b-24px flex items-center h-full">
          <MinusCircleOutlined @click="removeContent(row, index)" />
        </span>
      </span>
      </a-space-compact>
      <a-form-item label=" " :colon="false" class="p-r-22px">
        <a-button type="dashed" block :icon="h(PlusOutlined)" @click="appendContent">
          添加一条数据
        </a-button>
      </a-form-item>
    </template>
    <!-- 说明 -->
    <a-space-compact v-for="(row, index) in model.descriptions" :key="row.id" block>
      <a-form-item
        :label="index ? ' ' : '说明'" :colon="!index"
        :name="['descriptions', index, 'name']"
        :rules="{ validator: groupValidatorDescription, message: '请输入标题' }"
      >
        <a-input v-model:value.trim="model.descriptions[index].name" placeholder="标题" />
      </a-form-item>
      <a-form-item
        class="w-80%"
        :name="['descriptions', index, 'description']"
        :rules="{ validator: groupValidatorDescription, message: '请输入内容' }"
      >
        <a-textarea v-model:value="row.description" placeholder="内容" :auto-size="{ minRows: 1, maxRows: 10 }" />
      </a-form-item>
      <span class="m-l-8px">
        <span class="p-b-24px flex items-center h-full">
          <MinusCircleOutlined @click="removeDescription(row, index)" />
        </span>
      </span>
    </a-space-compact>
    <a-form-item label=" " :colon="false" class="p-r-22px">
      <a-button type="dashed" block :icon="h(PlusOutlined)" @click="appendDescription">
        添加一条说明
      </a-button>
      <div></div>
    </a-form-item>
    <a-form-item
      name="count"
      :rules="{ validator: dataValidValidator, message: '数据或者说明至少需要添加一项' }"
    >
      <a-space>
        <a-button type="primary" html-type="submit" @click="nextDestroy=false">保存</a-button>
        <a-button type="default" html-type="submit" @click="nextDestroy=true">保存并关闭</a-button>
        <a-button type="dashed" @click="emits('destroy')">关闭</a-button>
      </a-space>
    </a-form-item>
  </a-form>
</template>
<style scoped lang="scss">
.form-wrapper {
  padding: 12px 24px;

  :deep(.ant-form-item) {
    &.max {
      flex: 1 1 80%;
    }

    .ant-form-item-label {
      width: 80px;
    }
  }

}
</style>
