<template>
  <app-dialog v-model="visible" :loading="loading" :title="title" append-to-body class="detail-dialog" hide-footer width="70%">
    <div class="flex-col-sb h-100">
      <el-form
        ref="form"
        :disabled="dataSourceDisabled"
        :model="sourceProp"
        class="flex-1 overflow-y source-form ml-15"
        label-width="150px"
        label-position="right"
      >
        <div class="px-15 ml-15">
          <el-row>
            <el-form-item
              :rules="[
                {
                  required: true,
                  message: '请输入名称',
                  trigger: 'blur',
                },
              ]"
              label="数据源连接中文名"
              prop="name"
            >
              <el-input v-model="sourceProp.name" :maxlength="50" autocomplete="off" clearable placeholder="请输入" show-word-limit />
            </el-form-item>
            <el-form-item
              :rules="[
                {
                  required: true,
                  pattern: /^[a-zA-Z][a-zA-Z0-9_]*$/,
                  message: '只能输入字母、数字、下划线，且以字母开头',
                },
              ]"
              label="数据库连接名(唯一)"
              prop="alias"
            >
              <el-input v-model="sourceProp.alias" :maxlength="50" clearable placeholder="请输入" show-word-limit v-pinyin="sourceProp.name" />
            </el-form-item>
          </el-row>

          <el-form-item :rules="[{ required: true, message: '请选择数据库类型', trigger: 'change' }]" label="数据库类型" prop="dbType">
            <el-radio-group v-model="sourceProp.dbType" @change="currentSel">
              <el-radio-button v-for="radio in DICT.DB_TYPE" :key="radio.value" :label="radio.value">{{ radio.value }} </el-radio-button>
            </el-radio-group>
          </el-form-item>
        </div>
        <div class="px-15 ml-15">
          <el-row :gutter="15">
            <el-col :span="6">
              <el-form-item label="IP">
                <el-input v-model.trim="ip" autocomplete="off" clearable placeholder="请输入IP" @blur="changeDefault('ip')" />
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="Port">
                <el-input v-model.trim="port" autocomplete="off" clearable placeholder="请输入Port" @blur="changeDefault('port')" />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="数据库实例">
                <el-input v-model.trim="instance" autocomplete="off" clearable placeholder="请输入数据库实例" @blur="changeDefault('instance')" />
              </el-form-item>
            </el-col>
          </el-row>
        </div>

        <div v-if="sourceProp.settingJson.length" class="px-15 ml-20">
          <el-row :gutter="15" class="">
            <template v-for="(item, index) in sourceProp.settingJson">
              <el-form-item
                v-if="['driverClass', 'username', 'password'].includes(item.name)"
                :key="`col_${index}`"
                :label="item.comment"
                :prop="item.name"
                :rules="[
                  {
                    required: ['driverClass', 'username', 'password'].includes(item.name),
                    message: `请输入${item.name}`,
                    trigger: 'blur',
                  },
                ]"
              >
                <el-input
                  v-model="item.value"
                  :disabled="['password', 'connectionProperties'].includes(item.name) && isEncrypt === 1"
                  :placeholder="'请输入' + item.comment"
                  clearable
                  show-word-limit
                  @change="(v) => onSettingChange(v, item)"
                />
                ({{ item.type }})
                <el-switch
                  v-if="item.name == 'password'"
                  v-model="isEncrypt"
                  :active-value="1"
                  :inactive-value="0"
                  active-color="#13ce66"
                  active-text="加密"
                  class="ml-15"
                  inactive-color="#ff4949"
                  inactive-text="不加密"
                  @change="encrypt(item)"
                />
              </el-form-item>
            </template>
            <template v-for="(item, index) in sourceProp.settingJson">
              <el-form-item
                v-if="['url'].includes(item.name)"
                :key="`col_${index}`"
                :label="item.comment"
                :prop="item.name"
                :rules="[
                  {
                    required: ['url'].includes(item.name),
                    message: `请输入${item.name}`,
                    trigger: 'blur',
                  },
                ]"
              >
                <el-input
                  class="url"
                  :class="{ url: item.name === 'url' }"
                  type="textarea"
                  v-model="item.value"
                  :disabled="['password', 'connectionProperties'].includes(item.name) && isEncrypt === 1"
                  :placeholder="'请输入' + item.comment"
                  clearable
                  show-word-limit
                  @change="(v) => onSettingChange(v, item)"
                />
                ({{ item.type }})
              </el-form-item>
            </template>
            <ht-form-item label="是否启用">
              <el-switch v-model="sourceProp.enabled" active-text="是" inactive-text="否" />
            </ht-form-item>
          </el-row>
        </div>
      </el-form>
      <div class="mt-10 text-r ma-c">
        <el-button :disabled="!sourceProp.settingJson.length" :loading="submitLoading" size="mini" type="primary" @click="handleSave('check')">
          测试连接
        </el-button>
        <el-button :disabled="!sourceProp.settingJson.length" :loading="submitLoading" size="mini" type="primary" @click="handleSave('save')">保存 </el-button>
      </div>
    </div>
    <!--    <template #footer>-->
    <!--      <div class="mt-10 text-r">-->
    <!--        <el-button :disabled="!sourceProp.settingJson.length" :loading="submitLoading" size="mini" type="primary" @click="handleSave('check')">-->
    <!--          测试连接-->
    <!--        </el-button>-->
    <!--        <el-button :disabled="!sourceProp.settingJson.length" :loading="submitLoading" size="mini" type="primary" @click="handleSave('save')">保存 </el-button>-->
    <!--      </div>-->
    <!--    </template>-->
  </app-dialog>
</template>

<script>
import dialogMixin from '@/mixins/dialog.js';
import API from '@/api/business/changeDataSource';
import DICT from '@/utils/dict';
import { get } from 'lodash';

export default {
  mixins: [dialogMixin],
  props: {
    sysDataSource: {
      type: Array,
      default: () => [],
    },
    data: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      DICT,
      sourceProp: {
        name: '',
        alias: '',
        settingJson: [],
        enabled: false,
        dbType: 'Mysql',
        datasourcesType: 1,
        url: '',
        driverClass: '',
        validationQuery: '',
        username: '',
        password: '',
      },
      isEncrypt: 0,
      rowMapArr: [],
      newSourceProp: {},
      isSubmit: false,
      dataSourceDisabled: false,
      submitLoading: false,
      ip: '{ip}',
      port: '{port}',
      instance: '{instance}',
      currentData: null,
    };
  },
  methods: {
    initData(data) {
      this.currentData = data;
      let settingJson = '';
      if (data) {
        this.sourceProp = { ...this.sourceProp, ...data };
        settingJson = data?.settingJson;
      } else {
        this.sourceProp = this.$options.data().sourceProp;
        settingJson = this.sysDataSource[0]?.settingJson;
      }
      this.$nextTick(() => {
        this.$refs.form.clearValidate();
      });

      this.settingSel(settingJson);
    },
    onSettingChange(v, item) {
      this.sourceProp[item.name] = v;
      console.log(this.sourceProp);
    },
    encrypt(item) {
      if (this.isEncrypt === 1) {
        if (!item.value) {
          this.$message.warning('请输入密码!');
          this.isEncrypt = 0;
          return;
        }
        this.$http
          .post(`${window.context.portal}/base/tools/v1/encryptDbPassword`, {
            password: item.value,
          })
          .then((res) => {
            if (!res.data.state) {
              this.$message.error('加密失败');
              return;
            }
            item.value = res.data.value.password;
            const publicKey = {
              name: 'connectionProperties',
              comment: '公钥',
              type: 'java.lang.String',
              baseAttr: '1',
              default: '',
              value: `config.decrypt=true;config.decrypt.key=${res.data.value.publicKey}`,
            };
            this.sourceProp.settingJson.splice(
              this.sourceProp.settingJson.findIndex((item) => item.name === 'connectionProperties'),
              1,
            );
            this.sourceProp.settingJson.splice(this.sourceProp.settingJson.findIndex((item) => item.name === 'password') + 1, 0, publicKey);
          });
      } else {
        item.value = '';
        const index = this.sourceProp.settingJson.findIndex((item) => item.name === 'connectionProperties');
        if (index > 0) {
          this.sourceProp.settingJson.splice(index, 1);
        }
      }
    },

    changeDefault(type) {
      if (this[type]) {
        this.currentSel(this.sourceProp.dbType);
      } else {
        this[type] = '{' + type + '}';
        this.currentSel(this.sourceProp.dbType);
      }
    },

    currentSel(val) {
      const d = DICT.DB_TYPE_LIST.find((item) => item.value === val);
      if (!d) return;
      for (let j = 0; j < this.sourceProp.settingJson.length; j++) {
        const attr = this.sourceProp.settingJson[j];
        if (attr.name.toLowerCase().indexOf('url') > -1) {
          attr.value = this.formatURL(val, d.url);
          this.sourceProp.url = this.formatURL(val, d.url);
        } else if (attr.name.toLowerCase().indexOf('driver') > -1) {
          attr.value = d?.driverName;
          this.sourceProp.driverClass = d?.driverName;
        } else if (attr.name.toLowerCase().indexOf('validationquery') > -1) {
          attr.value = d?.validationquery;
          this.sourceProp.validationQuery = d?.validationquery;
        }
      }
    },

    formatURL(type) {
      let newURL = '';
      if (type === 'Mysql') {
        newURL = `jdbc:mysql://${this.ip}:${this.port}/${this.instance}?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=convertToNull`;
      } else if (type === 'Oracle') {
        newURL = `jdbc:oracle:thin:@${this.ip}:${this.port}:${this.instance}`;
      } else if (type === 'SqlServer') {
        newURL = `jdbc:sqlserver://${this.ip}:${this.port};databaseName=${this.instance}`;
      } else if (type === 'PostgreSql') {
        newURL = `jdbc:postgresql://${this.ip}:${this.port}/${this.instance}`;
      }
      return newURL;
    },

    settingSel(settingJson) {
      if (settingJson) {
        const settingJson_ = JSON.parse(settingJson.replace(/[\\]/g, ''));

        if (this.currentData) {
          this.getDefaultData(settingJson_);
        } else {
          this.ip = '{ip}';
          this.port = '{port}';
          this.instance = '{instance}';
        }

        // 改变了数据池id，那么需要输入的属性也变了
        const def = this.sysDataSource[0];
        this.sourceProp.classPath = def.classPath;
        this.sourceProp.initMethod = def.initMethod;
        this.sourceProp.closeMethod = def.closeMethod;
        // 处理配置的初始化值
        this.sourceProp.settingJson = [];
        settingJson_.forEach((obj) => {
          obj.value = obj.value || obj['default'];
          this.sourceProp.settingJson.push(obj);
        });

        // 选择了数据源类型 就替换数据源中的连接地址和驱动
        this.currentSel(this.sourceProp.dbType);

        // 数据源配置别名跟这里的别名一致
        for (let i = 0; i < this.sourceProp.settingJson.length; i++) {
          const attr = this.sourceProp.settingJson[i];
          if (attr.name.toLowerCase().indexOf('alias') > -1) {
            attr.value = attr.alias;
          }
          if (['username', 'password'].includes(attr.name.toLowerCase())) {
            this.sourceProp[attr.name] = attr.value;
          }
        }
      }
    },

    getDefaultData(settingJson) {
      if (settingJson) {
        const target = settingJson.filter((item) => item.name === 'url');
        if (target) {
          let url = target[0].value;
          const { ip = '${ip}', port = '${port}', instance = '${instance}' } = this.analysisURL(url);
          this.ip = ip;
          this.port = port;
          this.instance = instance;
        }
      }
    },

    analysisURL(url) {
      let res;
      switch (this.currentData.dbType) {
        case 'Mysql':
          res = this.analysisMysql(url);
          break;
        case 'Oracle':
          res = this.analysisOracle(url);
          break;
        case 'SqlServer':
          res = this.analysisSqlServer(url);
          break;
        case 'PostgreSql':
          res = this.analysisPostgreSql(url);
          break;
        default:
          break;
      }
      return res;
    },

    analysisMysql(url) {
      let ip, port, instance;
      let a = url.split('//')[1].split('?')[0];
      ip = a.split(':')[0] || '${ip}';
      port = a.split(':')[1].split('/')[0] || '${port}';
      instance = a.split(':')[1].split('/')[1] || '${instance}';
      return {
        ip,
        port,
        instance,
      };
    },

    analysisOracle(url) {
      let ip, port, instance;
      let a = url.split('@')[1];
      ip = a.split(':')[0] || '${ip}';
      port = a.split(':')[1] || '${port}';
      instance = a.split(':')[2] || '${instance}';
      return {
        ip,
        port,
        instance,
      };
    },

    analysisSqlServer(url) {
      let ip, port, instance;
      let a = url.split('//')[1].split(';')[0];
      ip = a.split(':')[0] || '${ip}';
      port = a.split(':')[1] || '${port}';
      instance = url.split('=')[1] || '${instance}';
      return {
        ip,
        port,
        instance,
      };
    },

    analysisPostgreSql(url) {
      let ip, port, instance;
      let a = url.split('//')[1];
      ip = a.split(':')[0] || '${ip}';
      port = a.split(':')[1].split('/')[0] || '${port}';
      instance = a.split(':')[1].split('/')[1] || '${instance}';
      return {
        ip,
        port,
        instance,
      };
    },

    async handleSave(type) {
      this.$refs.form.validate(async (valid) => {
        if (valid) {
          const config = {
            check: {
              defaultMsg: '测试通过',
              request: API.checkConnection,
            },
            save: {
              defaultMsg: '数据源已保存',
              request: API.saveSource,
            },
          };
          const cur = config[type];
          const newSourceProp = { ...this.sourceProp, settingJson: JSON.stringify(this.sourceProp.settingJson) };

          try {
            this.submitLoading = true;
            const { state, message } = await cur.request(newSourceProp);
            if (state) {
              this.$message.success(message || cur.defaultMsg);
              if (type === 'save') {
                this.visible = false;
                this.$emit('onConfirm', newSourceProp);
              }
            }
          } catch (error) {
            console.log(error);
          } finally {
            this.submitLoading = false;
          }
        }
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.title {
  position: relative;
}

.line-split {
  height: 80%;
  width: 1px;
  background: #eee;
}

.source-form {
  ::v-deep {
    .el-input {
      max-width: 400px;
    }
    .el-input.url {
      max-width: calc(100% - 160px);
    }
  }
}

::v-deep {
  .el-form-item {
    margin-bottom: 20px;
  }

  .el-form-item__label {
    width: 150px !important;
    overflow: hidden;
    word-break: break-all;
    display: flex;
    align-items: center;
    min-height: 32px;
    justify-content: end;
    line-height: normal;
  }

  .el-form-item__error {
    left: 0 !important;
  }

  .el-timeline-item__wrapper {
    height: 100%;
    display: flex;
    flex-direction: column;

    .el-timeline-item__content {
      flex: 1;
    }
  }

  .el-timeline-item__timestamp {
    font-size: 15px;
    font-weight: bold;
    padding-top: 2px !important;
  }

  .el-card__body {
    height: 100%;
    box-sizing: border-box;
    padding: 15px;
  }
}

.el-timeline {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  height: 100%;
}
</style>
