<template>
  <el-dialog
    class="dialog-simple"
    :visible="dialogVisible"
    :title="title"
    :width="configs.dialogWidth ?? '50%'"
    v-dialogDrag
    append-to-body>
    <!-- @close="handleClose"
    :destroy-on-close="false"
    :close-on-click-modal="configs.clickModalToClose ?? true" -->

    <!-- form  -->
    <el-form ref="dataForm" :model="dataForm" v-loading="loadingStatus">
      <el-row v-for="(row, rowIndex) in configs.form.rows" :key="'row_' + rowIndex" :gutter="20">
        <el-col v-for="(col, colIndex) in row" :key="colIndex" :span="24 / row.length">
          <!-- 通过多个 col === null 可以控制更灵活的 span 大小 -->
          <el-form-item v-if="col !== null" :label="col.label" :prop="col.prop" :rules="col.rules || null">
            <el-input
              v-if="col.input"
              v-model="dataForm[col.prop]"
              clearable
              :disabled="IsNotAvaliable(col)"
              v-bind="col.elparams" />
            <el-input
              v-if="col.forceDisabled && col.eraseOnSubmit"
              v-model="shadowDataForm[col.prop]"
              disabled
              v-bind="col.elparams" />
            <el-input
              v-if="col.forceDisabled && !col.eraseOnSubmit"
              v-model="dataForm[col.prop]"
              disabled
              v-bind="col.elparams" />
            <el-button
              type=""
              plain
              v-if="col.button"
              v-bind="col.elparams"
              style="width: 100%"
              @click="handleButtonClick(col)">
              {{ col.label }}
            </el-button>
            <el-cascader
              v-if="col.cascader"
              v-model="dataForm[col.prop]"
              :options="col.options"
              :disabled="IsNotAvaliable(col)"
              v-bind="col.elparams"></el-cascader>
            <el-select
              v-if="col.forceDisabledSelect"
              disabled
              v-model="dataForm[col.prop]"
              clearable
              v-bind="col.elparams">
              <el-option
                v-for="(opt, optIdx) in col.options"
                :key="'option_' + optIdx"
                :label="opt.label"
                :value="opt.value" />
            </el-select>
            <el-select
              v-if="col.select"
              v-model="dataForm[col.prop]"
              clearable
              :disabled="IsNotAvaliable(col)"
              v-bind="col.elparams"
              @change="handleSelectChange(col, $event)">
              <el-option
                v-for="(opt, optIdx) in col.options"
                :key="'option_' + optIdx"
                :label="opt.label"
                :value="opt.value">
                <span>{{ opt.label }}</span>
                <span
                  v-if="col.customLabel"
                  style="display: inline-clock; margin-left: 12px; font-size: 0.9em; color: #ccce">
                  {{ opt[col.customLabel] || "-" }}
                </span>
              </el-option>
            </el-select>
            <el-switch
              v-if="col.switch"
              v-model="dataForm[col.prop]"
              :active-value="col.activeValue ?? 1"
              :inactive-value="col.activeValue ?? 0"
              @change="handleSwitchChange"
              :disabled="detailMode" />
            <el-input
              v-if="col.textarea"
              type="textarea"
              v-model="dataForm[col.prop]"
              :disabled="IsNotAvaliable(col)"
              v-bind="col.elparams" />
            <el-date-picker
              v-if="col.datetime"
              v-model="dataForm[col.prop]"
              :disabled="IsNotAvaliable(col)"
              v-bind="col.elparams" />
            <!-- <uploadBtn
              v-if="col.upload"
              :key="'upload_' + rowIndex + colIndex"
              :action="col.actionUrl"
              :file-list="dataForm['files']"
              :disabled="IsNotAvaliable(col)"
              v-bind="col.elparams"
              @update-file-list="handleFilelistUpdate" />

            <quillEditor
              v-if="col.richInput"
              ref="quill-editor"
              v-model="dataForm[col.prop]"
              :options="col.quillConfig ?? defaultQuillConfig"
              style="margin-top: 42px"
              :disabled="IsNotAvaliable(col)" /> -->

            <div class="" v-if="col.component" style="margin: 42px 0 0">
              <!-- 下面这个 component 几乎是为 富文本 quill 定制的了... TODO：后续可能会根据业务需求创建新的版本  -->
              <component
                :is="col.component"
                :key="'component_' + col.prop"
                @update:modelValue="handleComponentModelUpdate(col.prop, $event)"
                :modelValue="dataForm[col.prop]"
                :mode="detailMode ? 'detail' : dataForm.id ? 'edit' : 'create'" />
            </div>
            <!-- add more...  -->
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>

    <!-- footer  -->
    <div slot="footer">
      <template v-for="(operate, index) in configs.form.operations">
        <el-button
          v-if="showButton(operate)"
          :key="'operation_' + index"
          :type="operate.type"
          @click="handleBtnClick(operate)">
          {{ operate.label }}
        </el-button>
      </template>
      <el-button @click="handleBtnClick({ name: 'cancel' })">取消</el-button>
    </div>
  </el-dialog>
</template>

<script>
// import { pick as __pick } from "@/utils/filters";
// import Cookies from "js-cookie";
// import uploadBtn from "@/components/uploadBtn";
// import moment from "moment";

// import "quill/dist/quill.core.css";
// import "quill/dist/quill.snow.css";
// import "quill/dist/quill.bubble.css";

// import { quillEditor } from "vue-quill-editor";

export default {
  name: "DialogJustForm",
  // components: { uploadBtn, quillEditor },
  props: {
    configs: {
      type: Object,
      default: () => ({
        clickModalToClose: true,
        forms: null,
      }),
    },
    title: {
      type: String,
      default: 'Title'
    },
    dialogVisible: {
      type: Boolean,
      default: false,
    },
    // extraParams: {
    //   type: Object,
    //   default: () => ({})
    // }
  },
  data() {
    const dataForm = {};
    const shadowDataForm = {};
    const savedDatalist = {};
    const cachedList = {};
    const watchList = [];

    this.configs.form.rows.forEach((row) => {
      row.forEach((col) => {
        if (!col.eraseOnSubmit && col.prop) dataForm[col.prop] = col.default ?? null;
        else shadowDataForm[col.prop] = col.default ?? null;

        if ("autoUpdateProp" in col) {
          savedDatalist[col.prop] = [];
        }
      });
    });

    if (this.configs.extraFields)
      this.configs.extraFields.forEach((cnf) => {
        dataForm[cnf.prop] = cnf.default ?? null;
      });

    return {
      loadingStatus: false,
      dataForm,
      shadowDataForm,
      savedDatalist,
      cachedList,
      watchList,
      detailMode: false,
      editMode: false,
      baseDialogConfig: null,
      defaultQuillConfig: {
        modules: {
          toolbar: [
            [{ font: [] }],
            [{ size: ["small", false, "large", "huge"] }], // custom dropdown
            ["bold", "italic", "underline", "strike"], // toggled buttons
            [{ color: [] }, { background: [] }], // dropdown with defaults from theme
            ["blockquote", "code-block"],
            [{ header: 1 }, { header: 2 }], // custom button values
            [{ list: "ordered" }, { list: "bullet" }],
            // [{ 'script': 'sub'}, { 'script': 'super' }],      // superscript/subscript
            [{ indent: "-1" }, { indent: "+1" }], // outdent/indent
            // [{ 'direction': 'rtl' }],                         // text direction
            // [{ 'header': [1, 2, 3, 4, 5, 6, false] }],
            // [{ 'align': [] }],
            // ['clean']                                         // remove formatting button
          ],
        },
        theme: "snow",
        readOnly: false,
        placeholder: "在这里输入描述信息...",
        scrollingContainer: null,
      },
    };
  },
  mounted() {
    /** 临时保存所有发出的请求 */
    const promiseHistory = {};

    const getData = (col, param) => {
      console.log("getData: ", col.prop, "/", param ?? "no param!");
      // 获取数据 - 不需要等待前置条件时
      promiseHistory[col.prop] = col.fetchData(param).then(({ data: res }) => {
        if (res.code === 0) {
          if ("list" in res.data) {
            console.log(
              "SdASD ",
              res.data.list.map((i) => ({
                label: col.optionLabel ? i[col.optionLabel] : i.name,
                value: col.optionValue ? i[col.optionValue] : i.id,
                [col.customLabel]: i[col.customLabel],
              }))
            );
            // 填充 options
            this.$set(
              col,
              "options",
              col.customLabel
                ? res.data.list.map((i) => ({
                    label: col.optionLabel ? i[col.optionLabel] : i.name,
                    value: col.optionValue ? i[col.optionValue] : i.id,
                    [col.customLabel]: i[col.customLabel],
                  }))
                : res.data.list.map((i) => ({
                    label: col.optionLabel ? i[col.optionLabel] : i.name,
                    value: col.optionValue ? i[col.optionValue] : i.id,
                  }))
            );

            // 是否需要缓存数据列表
            if ("injectTo" in col || col.cached) {
              this.cachedList[col.prop] = res.data.list;
            }

            // 设置监听
            if ("injectTo" in col) {
              console.log("set watcher: ", col.prop);

              const valueProp = "optionValue" in col ? col.optionValue : "id";
              const unwatch = this.$watch(
                () => this.dataForm[col.prop],
                (val) => {
                  console.log("do watcher: ", col.prop);
                  if (col.disableWatcherOnEdit && this.editMode) return;
                  if (!val) {
                    col.injectTo.map((item) => {
                      this.$set(this.dataForm, item[0], null);
                      this.$forceUpdate();
                    });
                    return;
                  }
                  const chosenObject = this.cachedList[col.prop].find((i) => i[valueProp] === val);
                  if (chosenObject) {
                    col.injectTo.map((item) => {
                      this.$set(this.dataForm, item[0], chosenObject[item[1]]);
                      this.$forceUpdate();
                    });
                  } else {
                    console.log('[x] if ("injectTo" in col) {');
                  }
                },
                {
                  immediate: false,
                }
              );
              this.watchList.push(unwatch);
            }

            if ("autoUpdateProp" in col) {
              this.$set(this.savedDatalist, col.prop, res.data.list);
            }

            // end branch
          } else if (Array.isArray(res.data)) {
            // 填充 options
            this.$set(
              col,
              "options",
              col.customLabel
                ? res.data.map((i) => ({
                    label: col.optionLabel ? i[col.optionLabel] : i.name,
                    value: col.optionValue ? i[col.optionValue] : i.id,
                    [col.customLabel]: i[col.customLabel],
                  }))
                : res.data.map((i) => ({
                    label: col.optionLabel ? i[col.optionLabel] : i.name,
                    value: col.optionValue ? i[col.optionValue] : i.id,
                  }))
            );

            // 是否需要缓存数据列表
            if ("injectTo" in col || col.cached) {
              this.cachedList[col.prop] = res.data;
            }

            // 设置监听
            if ("injectTo" in col && !col.watcher) {
              console.log("set watcher: ", col.prop);

              const valueProp = "optionValue" in col ? col.optionValue : "id";
              const unwatch = this.$watch(
                () => this.dataForm[col.prop],
                (val) => {
                  if (col.disableWatcherOnEdit && this.editMode) return;
                  console.log("do watcher: ", col.prop);
                  if (!val) {
                    col.injectTo.map((item) => {
                      this.$set(this.dataForm, item[0], null);
                      this.$forceUpdate();
                    });
                    return;
                  }
                  const chosenObject = this.cachedList[col.prop].find((i) => i[valueProp] === val);
                  if (chosenObject) {
                    col.injectTo.map((item) => {
                      this.$set(this.dataForm, item[0], chosenObject[item[1]]);
                      this.$forceUpdate();
                    });
                  } else {
                    console.log('[x] if ("injectTo" in col) {');
                  }
                },
                {
                  immediate: false,
                }
              );
              this.$set(col, "watcher", unwatch);
              this.watchList.push(unwatch);
            }

            if ("autoUpdateProp" in col) {
              this.$set(this.savedDatalist, col.prop, res.data);
            }
          }
          // end branch
        } else {
          col.options.splice(0);
        }
      });

      console.log("after getData: ", promiseHistory);
    };

    /** 处理函数 */
    const handleFn = (prevProp, anchorField, anchorValue, targetField, col) => {
      console.log("[handleFn]: ", prevProp, anchorField, anchorValue, targetField);
      /** 此时 cachedList 已经确保可用了 */
      const target = this.cachedList[prevProp].find((i) => i[anchorField] === anchorValue);
      const param = target ? target[targetField] : "";
      console.log("((( chosenObject )))", target);

      getData(col, param);
    };

    this.configs.form.rows.forEach((row) => {
      row.forEach((col) => {
        if (col.fetchData && typeof col.fetchData === "function" && !col.hasPrev) {
          getData(col);
        }
      });
    });

    /** 必须要遍历两遍 */
    this.configs.form.rows.forEach((row) => {
      row.forEach((col) => {
        if (col.fetchData && typeof col.fetchData === "function" && col.hasPrev) {
          console.log("[hasPrev] set watcher: ", col.hasPrev);

          // 获取数据 - 需要等待前置条件时
          const unwatch = this.$watch(
            () => this.dataForm[col.hasPrev],
            (val) => {
              console.log("[hasPrev] do watcher: ", col.hasPrev);
              if (!val) {
                col.injectTo.map((item) => {
                  this.$set(this.dataForm, item[0], null);
                  this.$forceUpdate();
                });
                return;
              }

              if (!this.editMode) this.$set(this.dataForm, col.prop, null);

              const { search, get } = col.fetchDataParam; // 伴随着 hasPrev 出现的

              if (!this.cachedList[col.hasPrev]) {
                // 如果 prev 还尚不存在，但此时 promiseHistory 一定存在了
                promiseHistory[col.hasPrev].then(() => {
                  handleFn(col.hasPrev, search, val, get, col);
                });
              } else {
                handleFn(col.hasPrev, search, val, get, col);
              }
            },
            {
              immediate: false,
            }
          );
          this.watchList.push(unwatch);
        }
      });
    });

    if (this.configs.extraFields)
      this.configs.extraFields.forEach((cnf) => {
        if (cnf.listenTo) {
          console.log("set watcher for: ", cnf.prop);
          const unwatch = this.$watch(
            () => this.dataForm[cnf.listenTo.prop],
            (carId) => {
              console.log("do watcher for: ", cnf.prop);
              if (!carId) return;
              if (cnf.disableWatcherOnEdit && this.editMode) return;
              cnf.listenTo.handler.call(this, this.cachedList[cnf.listenTo.prop], carId);
            },
            {
              immediate: false,
            }
          );
          this.watchList.push(unwatch);
        }
      });
  },
  computed: {
    uploadHeaders() {
      return {
        token: Cookies.get("token") || "",
      };
    },
  },
  methods: {
    IsNotAvaliable(col) {
      return this.detailMode || (col.disableOnEdit && this.editMode);
    },
    handleFilelistUpdate(newFilelist) {
      // TODO: 直接访问 .files 可能不太合适
      this.dataForm.files = newFilelist.map((file) => ({
        id: file.id,
        name: file.name,
        url: file.url,
        typeCode: file.typeCode,
      }));
      // 更新请求
      if ("id" in this.dataForm && this.dataForm.id !== null && this.dataForm.id !== undefined) this.addOrUpdate("PUT");
      else
        this.$notify({
          title: "等待保存",
          message: "已添加文件，请手动点击保存！",
          type: "warning",
          duration: 2500,
        });
    },

    /** utitilities */
    showButton(operate) {
      const notDetailMode = !this.detailMode;
      const showAlways = operate.showAlways ?? false;
      const editMode = operate.showOnEdit && this.dataForm.id;
      const addMode = !operate.showOnEdit && !this.dataForm.id;
      const permission = operate.permission ? this.$hasPermission(operate.permission) : true;
      return notDetailMode && (showAlways || ((editMode || addMode) && permission));
    },

    resetForm(excludeId = false, immediate = false) {
      setTimeout(
        () => {
          Object.keys(this.dataForm).forEach((key) => {
            if (excludeId && key === "id") return;
            this.dataForm[key] = null;
          });
          Object.keys(this.shadowDataForm).forEach((key) => {
            this.shadowDataForm[key] = null;
          });
          this.$refs.dataForm.clearValidate();
          this.$emit("dialog-closed"); // 触发父组件销毁自己
        },
        immediate ? 0 : 200
      );
    },

    /** init **/
    init(id, detailMode, tagInfo, extraParams) {
      // console.log("[DialogJustForm] init", this.dataForm, id, detailMode);
      if (this.$refs.dataForm) {
        // console.log("[DialogJustForm] clearing form validation...");
        // 当不是首次渲染dialog的时候，一开始就清空验证信息，本组件的循环里只有一个 dataForm 所以只用取 [0] 即可
        this.$refs.dataForm.clearValidate();
      }

      this.detailMode = detailMode ?? false;
      this.editMode = id ? true : false;

      /** 判断 extraParams */
      if (extraParams && typeof extraParams === "object") {
        for (const [key, value] of Object.entries(extraParams)) {
          // console.log("[dialog] dataForm | key | value", this.dataForm, key, value);
          this.$set(this.dataForm, key, value);
        }
      }

      this.$nextTick(() => {
        this.dataForm.id = id || null;
        if (this.dataForm.id) {
          // 如果是编辑
          this.loadingStatus = true;

          // 获取基本信息
          this.$http
            .get(this.urls.base + `/${this.dataForm.id}`)
            .then(({ data: res }) => {
              if (res && res.code === 0) {
                if (res.data) this.dataForm = __pick(res.data, Object.keys(this.dataForm));
                else {
                  this.$message({
                    message: "后端返回的数据为 null",
                    type: "error",
                    duration: 2000,
                  });
                }
                /** 格式化文件上传列表 */
                if (Array.isArray(this.dataForm.files)) {
                  this.dataForm.files = this.dataForm.files.map((file) => ({
                    id: file.id,
                    name: file.fileUrl.split("/").pop(),
                    typeCode: file.typeCode,
                    url: file.fileUrl,
                  }));
                }
                // console.log("[DialogJustForm] init():", this.dataForm);
              } else {
                this.$message({
                  message: `${res.code}: ${res.msg}`,
                  type: "error",
                  duration: 1500,
                });
              }
              this.loadingStatus = false;
            })
            .catch((err) => {
              this.loadingStatus = false;
              this.$message({
                message: `${err}`,
                type: "error",
                duration: 1500,
              });
            });
        } else {
          this.loadingStatus = false;
        }
      });
    },

    handleButtonClick(col) {
      if (!("onClick" in col)) {
        console.log("[handleButtonClick] 配置文件config.js 里没有绑定 onClick");
        return;
      }

      col.onClick.call(this, this.dataForm.id);
    },

    /** handlers */
    handleSelectChange(col, eventValue) {
      if ("autoUpdateProp" in col) {
        // 自动更新 相关联 的字段
        // console.log(col.options, eventValue, this.savedDatalist);
        const item = this.savedDatalist[col.prop].find((item) => item.id === eventValue);
        this.shadowDataForm[col.autoUpdateProp] = item.cate ?? null;
      }
      this.$forceUpdate();
    },

    handleSwitchChange(val) {
      console.log("[dialog] switch change: ", val, this.dataForm);
    },

    handleComponentModelUpdate(propName, { subject, payload: { data } }) {
      this.dataForm[propName] = JSON.stringify(data);
      console.log("[DialogJustForm] handleComponentModelUpdate", this.dataForm[propName]);
    },

    addOrUpdate(method = "POST", url) {
      if ("parentId" in this.dataForm) {
        console.log("[DialogJustForm parentId]", this.dataForm.parentId);
        // 对特殊的键做特殊处理，如 parentId 是一个 cascader，获取的值是 ["xxx"]，后端只需要xxx
        const lastItem = this.dataForm.parentId.length - 1;
        this.dataForm.parentId = this.dataForm.parentId[lastItem];
      }

      this.$refs.dataForm.validate((passed, result) => {
        if (passed) {
          this.loadingStatus = true;

          let httpPayload = null;
          /** 针对有文件上传选项的弹窗提供特殊的 payload */
          if (this.dataForm.files) {
            httpPayload = {
              ...this.dataForm,
              fileIds: this.dataForm.files.map((file) => file.id),
            };
          } else {
            httpPayload = this.dataForm;
          }

          /** 针对时间段设置 payload */
          if ("startTime" in this.dataForm && "endTime" in this.dataForm) {
            const { startTime, endTime } = this.dataForm;
            httpPayload = {
              ...httpPayload,
              startTime: startTime
                ? moment(startTime).format("YYYY-MM-DDTHH:mm:ss")
                : moment().format("YYYY-MM-DDTHH:mm:ss"),
              endTime: endTime ? moment(endTime).format("YYYY-MM-DDTHH:mm:ss") : null, //  moment().format("YYYY-MM-DDTHH:mm:ss"),
            };
          }

          /** 针对时间段设置 payload */
          if ("updateTime" in this.dataForm) {
            const { updateTime } = this.dataForm;
            httpPayload = {
              ...httpPayload,
              updateTime: updateTime
                ? moment(updateTime).format("YYYY-MM-DDTHH:mm:ss")
                : moment().format("YYYY-MM-DDTHH:mm:ss"),
            };
          }

          /** 发送 */
          return this.$http({
            // url: this.urls.formUrl ? this.urls.formUrl : this.urls.base,
            url: url ?? this.urls.base,
            method,
            data: httpPayload,
          })
            .then(({ data: res }) => {
              console.log("[add&update] res is: ", res);
              this.loadingStatus = false;
              if (res.code === 0) {
                this.$message.success(method === "POST" ? "添加成功" : "更新成功");
                this.$emit("refreshDataList");
                // if (method === "POST") this.handleClose();
                this.handleClose();
              } else {
                this.$message({
                  message: `${res.code}: ${res.msg}`,
                  type: "error",
                  duration: 2000,
                });
              }
            })
            .catch((errMsg) => {
              this.$message.error("参数错误：" + errMsg);
              if (this.loadingStatus) this.loadingStatus = false;
            });
        } else {
          this.$message.error("请核查字段信息");
        }
      });
    },

    handleBtnClick(payload) {
      console.log("btn click payload: ", payload);

      if ("name" in payload) {
        switch (payload.name) {
          case "cancel":
            this.handleClose();
            break;
          case "reset":
            this.resetForm(true, true); // true means exclude id, immediate execution
            break;
          case "add":
          case "update":
            this.addOrUpdate(payload.name === "add" ? "POST" : "PUT");
            break;
          case "add-pos-manually": {
            this.addOrUpdate("POST", this.urls.posFormUrl);
            break;
          }
          case "add-car-payload": {
            console.log("edit-car-payload", payload);
            this.addOrUpdate("POST", this.urls.payloadFormUrl);
            break;
          }
        }
      } else {
        console.log("[x] 不是这么用的! 缺少name属性");
      }
    },

    handleUploadChange(file, fileList) {
      console.log("[Upload] handleUploadChange...", file, fileList);
    },

    handleClose() {
      this.resetForm();
      this.$emit("update:dialogVisible", false);
    },
  },
};
</script>

<style scoped>
.dialog-simple >>> .el-dialog__body {
  /* padding-top: 16px !important;
  padding-bottom: 16px !important; */
  padding-top: 0 !important;
  padding-bottom: 0 !important;
}

.el-select,
.el-cascader,
.el-date-editor {
  width: 100% !important;
}

.dialog-simple >>> .el-dialog__header {
  padding: 10px 20px 10px;
  /* background: linear-gradient(to bottom, rgba(0, 0, 0, 0.25), white); */
}
</style>
