<script>
import { defineComponent } from 'vue';

import _ from 'lodash'
import JsonEditor from '@/view/components/JsonEditor.vue'
import { CheckOutlined, InfoCircleOutlined, PlusOutlined, MinusOutlined, SyncOutlined, ReloadOutlined } from '@ant-design/icons-vue'
import Plugin from '../mixins/plugin'

export default defineComponent({
  name: 'Server',

  components: {
    JsonEditor,
    CheckOutlined,
    InfoCircleOutlined,
    PlusOutlined,
    MinusOutlined,
    SyncOutlined,
    ReloadOutlined,
  },

  mixins: [Plugin],

  data () {
    return {
      key: 'server',
      activeTabKey: '1',
      dnsMappings: [],
      speedTestList: [],
      whiteList: [],
      speedRefreshInterval: null,
      whiteListOptions: [
        {
          label: '不代理',
          value: 'true',
        },
        {
          label: '代理',
          value: 'false',
        },
      ],
      familyOptions: [
        {
          label: 'IPv4',
          value: '4',
        },
        {
          label: 'IPv6',
          value: '6',
        },
      ],
    }
  },

  computed: {
    speedDnsOptions () {
      const options = []
      if (!this.config || !this.config.server || !this.config.server.dns || !this.config.server.dns.providers) {
        return options
      }
      _.forEach(this.config.server.dns.providers, (dnsConfig, key) => {
        options.push({
          value: key,
          label: key,
        })
      })
      return options
    },
  },

  created () {
  },

  mounted () {
    this.registerSpeedTestEvent()
  },

  beforeUnmount () {
    // 清理事件监听器
    this.$api.ipc.removeAllListeners('speed')
    if (this.speedRefreshInterval) {
      clearInterval(this.speedRefreshInterval)
    }
  },

  methods: {
    async onCrtSelect () {
      const value = await this.$api.fileSelector.open(this.config.server.setting.rootCaFile.certPath, 'file')
      if (value != null && value.length > 0) {
        this.config.server.setting.rootCaFile.certPath = value[0]
      }
    },
    async onKeySelect () {
      const value = await this.$api.fileSelector.open(this.config.server.setting.rootCaFile.keyPath, 'file')
      if (value != null && value.length > 0) {
        this.config.server.setting.rootCaFile.keyPath = value[0]
      }
    },
    ready () {
      this.initDnsMapping()
      this.initWhiteList()
      if (this.config.server.dns.speedTest.dnsProviders) {
        this.speedDns = this.config.server.dns.speedTest.dnsProviders
      }
    },
    async applyBefore () {
      this.submitDnsMappings()
      this.submitWhiteList()
      this.delEmptySpeedHostname()
    },
    async applyAfter () {
      if (this.status.server.enabled) {
        return this.$api.server.restart()
      }
    },
    // dnsMapping
    initDnsMapping () {
      this.dnsMappings = []
      const familyMapping = this.config.server.dns.familyMapping || {}
      for (const key in this.config.server.dns.mapping) {
        const value = this.config.server.dns.mapping[key]
        this.dnsMappings.push({
          key,
          value,
          family: `${familyMapping[key] || '4'}`, // 转成字符串
        })
      }
    },
    submitDnsMappings () {
      const dnsMapping = {}
      const familyMapping = {}
      for (const item of this.dnsMappings) {
        if (item.key) {
          const hostname = this.handleHostname(item.key)
          if (hostname) {
            dnsMapping[hostname] = item.value
            if (item.family === '6' || (this.config.server.dns.familyMapping != null && this.config.server.dns.familyMapping[hostname] != null)) {
              familyMapping[hostname] = item.family
            }
          }
        }
      }
      this.config.server.dns.mapping = dnsMapping
      this.config.server.dns.familyMapping = familyMapping
    },
    deleteDnsMapping (item, index) {
      this.dnsMappings.splice(index, 1)
    },
    addDnsMapping () {
      let defaultDns
      const dnsArr = ['quad9', 'safe360', 'aliyun']
      for (const dnsName of dnsArr) {
        if (this.config.server.dns.providers[dnsName]) {
          defaultDns = dnsName
          break
        }
      }

      this.dnsMappings.unshift({ key: '', value: defaultDns, family: '4' })
      this.focusFirst(this.$refs.dnsMappings)
    },

    // whiteList
    initWhiteList () {
      this.whiteList = []
      for (const key in this.config.server.whiteList) {
        const value = this.config.server.whiteList[key]
        this.whiteList.push({
          key: key || '',
          value: value === true ? 'true' : 'false',
        })
      }
    },
    addWhiteList () {
      this.whiteList.unshift({ key: '', value: 'true' })
      this.focusFirst(this.$refs.whiteList)
    },
    deleteWhiteList (item, index) {
      this.whiteList.splice(index, 1)
    },
    submitWhiteList () {
      const whiteList = {}
      for (const item of this.whiteList) {
        if (item.key) {
          const hostname = this.handleHostname(item.key)
          if (hostname) {
            whiteList[hostname] = (item.value === 'true')
          }
        }
      }
      this.config.server.whiteList = whiteList
    },
    getSpeedTestConfig () {
      return this.config.server.dns.speedTest
    },
    addSpeedHostname () {
      this.getSpeedTestConfig().hostnameList.unshift('')
      this.focusFirst(this.$refs.hostnameList)
    },
    delSpeedHostname (item, index) {
      this.getSpeedTestConfig().hostnameList.splice(index, 1)
    },
    delEmptySpeedHostname () {
      for (let i = this.getSpeedTestConfig().hostnameList.length - 1; i >= 0; i--) {
        const hostname = this.handleHostname(this.getSpeedTestConfig().hostnameList[i])
        if (!hostname) {
          this.getSpeedTestConfig().hostnameList.splice(i, 1)
        }
      }
    },
    reSpeedTest () {
      this.$api.server.reSpeedTest()
    },
    registerSpeedTestEvent () {
      const listener = async (event, message) => {
        console.log('get speed event', event, message)
        if (message.key === 'getList') {
          this.speedTestList = message.value
        }
      }
      this.$api.ipc.on('speed', listener)
      this.speedRefreshInterval = this.startSpeedRefreshInterval()
      this.reloadAllSpeedTester()
    },
    async reloadAllSpeedTester () {
      this.$api.server.getSpeedTestList()
    },
    startSpeedRefreshInterval () {
      return setInterval(() => {
        this.reloadAllSpeedTester()
      }, 5000)
    },
    async handleTabChange (key) {
      this.activeTabKey = key
      if (key !== '2' && key !== '3' && key !== '5' && key !== '6' && key !== '7') {
        // 没有 JsonEditor，启用SearchBar
        window.config.disableSearchBar = false
      } else {
        // 有 JsonEditor，禁用SearchBar
        window.config.disableSearchBar = true
      }
    },
  },
});
</script>

<template>
  <ds-container>
    <template #header>
      加速服务设置
    </template>

    <div style="height: 100%" class="json-wrapper">
      <a-tabs
        v-if="config"
        :default-active-key="activeTabKey"
        tab-position="left"
        :style="{ height: '100%' }"
        @change="handleTabChange"
      >
        <a-tab-pane key="1" tab="基本设置">
          <div v-if="activeTabKey === '1'" style="padding-right:10px">
            <a-form-item label="代理服务:" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-checkbox v-model:checked="config.server.enabled">
                随应用启动
              </a-checkbox>
              <a-tag v-if="status.server.enabled" color="green">
                当前已启动
              </a-tag>
              <a-tag v-else color="red">
                当前未启动
              </a-tag>
            </a-form-item>
            <a-form-item label="绑定IP" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-input v-model:value="config.server.host" spellcheck="false" />
              <div class="form-help">
                你可以设置为<code>0.0.0.0</code>，让其他电脑可以使用此代理服务
              </div>
            </a-form-item>
            <a-form-item label="代理端口" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-input-number v-model:value="config.server.port" :min="0" :max="65535" :precision="0" spellcheck="false" />
              <div class="form-help">
                修改后需要重启应用
              </div>
            </a-form-item>
            <hr>
            <a-form-item label="全局校验SSL" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-checkbox v-model:checked="config.server.setting.NODE_TLS_REJECT_UNAUTHORIZED">
                NODE_TLS_REJECT_UNAUTHORIZED
              </a-checkbox>
              <div class="form-help">
                高风险操作，没有特殊情况请勿关闭
              </div>
            </a-form-item>
            <a-form-item label="代理校验SSL" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-checkbox v-model:checked="config.server.setting.verifySsl">
                校验加速目标网站的ssl证书
              </a-checkbox>
              <div class="form-help">
                如果目标网站证书有问题，但你想强行访问，可以临时关闭此项
              </div>
            </a-form-item>
            <a-form-item label="根证书" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-input-search
                v-model:value="config.server.setting.rootCaFile.certPath" addon-before="Cert" enter-button="选择"
                :title="config.server.setting.rootCaFile.certPath" spellcheck="false"
                @search="onCrtSelect"
              />
              <a-input-search
                v-model:value="config.server.setting.rootCaFile.keyPath" addon-before="Key" enter-button="选择"
                :title="config.server.setting.rootCaFile.keyPath" spellcheck="false"
                @search="onKeySelect"
              />
            </a-form-item>
            <hr>
            <a-form-item label="启用拦截" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-checkbox v-model:checked="config.server.intercept.enabled">
                启用拦截
              </a-checkbox>
              <div class="form-help">
                关闭拦截，且关闭增强功能时，就不需要安装根证书，退化为安全模式
              </div>
            </a-form-item>
            <a-form-item label="启用脚本" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-checkbox v-model:checked="config.server.setting.script.enabled">
                允许插入并运行脚本
              </a-checkbox>
              <div class="form-help">
                关闭后，<code>Github油猴脚本</code>也将关闭
              </div>
            </a-form-item>
          </div>
        </a-tab-pane>
        <a-tab-pane key="2" tab="拦截设置">
          <div v-if="activeTabKey === '2'" style="height:100%">
            <JsonEditor
              v-model="config.server.intercepts" style="height:100%" mode="code"
              :show-btns="false" :expanded-on-start="true"
            />
          </div>
        </a-tab-pane>
        <a-tab-pane key="3" tab="超时时间设置">
          <div v-if="activeTabKey === '3'" style="height:100%;display:flex;flex-direction:column">
            <a-form-item label="默认超时时间" :label-col="labelCol" :wrapper-col="wrapperCol">
              请求：<a-input-number v-model:value="config.server.setting.defaultTimeout" :step="1000" :min="1000" :precision="0" spellcheck="false" /> ms，对应<code>timeout</code>配置<br>
              连接：<a-input-number v-model:value="config.server.setting.defaultKeepAliveTimeout" :step="1000" :min="1000" :precision="0" spellcheck="false" /> ms，对应<code>keepAliveTimeout</code>配置
            </a-form-item>
            <hr style="margin-bottom:15px">
            <div>这里指定域名的超时时间：<span class="form-help">（域名配置可使用通配符或正则）</span></div>
            <JsonEditor
              v-model="config.server.setting.timeoutMapping" style="flex-grow:1;min-height:300px;margin-top:10px" mode="code"
              :show-btns="false" :expanded-on-start="true"
            />
          </div>
        </a-tab-pane>
        <a-tab-pane key="4" tab="域名白名单">
          <div v-if="activeTabKey === '4'">
            <a-row style="margin-top:10px">
              <a-col span="21">
                <div>配置为<code>不代理</code>的域名不会通过代理</div>
              </a-col>
              <a-col span="3">
                <a-button style="margin-left:8px" type="primary" @click="addWhiteList()"><PlusOutlined /></a-button>
              </a-col>
            </a-row>
            <a-row v-for="(item, index) of whiteList" ref="whiteList" :key="index" :gutter="10" style="margin-top: 5px">
              <a-col :span="16">
                <a-input v-model:value="item.key" spellcheck="false" />
              </a-col>
              <a-col :span="5">
                <a-select v-model:value="item.value" class="w100">
                  <a-select-option v-for="(item2) of whiteListOptions" :key="item2.value" :value="item2.value">
                    {{ item2.label }}
                  </a-select-option>
                </a-select>
              </a-col>
              <a-col :span="3">
                <a-button type="danger" @click="deleteWhiteList(item, index)"><MinusOutlined /></a-button>
              </a-col>
            </a-row>
          </div>
        </a-tab-pane>
        <a-tab-pane key="5" tab="自动兼容程序">
          <div v-if="activeTabKey === '5'" style="height:100%;display:flex;flex-direction:column">
            <div>
              说明：<code>自动兼容程序</code>会自动根据错误信息进行兼容性调整，并将兼容设置保存在 <code>~/.dev-sidecar/automaticCompatibleConfig.json</code> 文件中。但并不是所有的兼容设置都是正确的，所以需要通过以下配置来覆盖错误的兼容设置。
            </div>
            <JsonEditor
              v-model="config.server.compatible" style="flex-grow:1;min-height:300px;margin-top:10px;" mode="code"
              :show-btns="false" :expanded-on-start="true"
            />
          </div>
        </a-tab-pane>
        <a-tab-pane key="6" tab="IP预设置">
          <div v-if="activeTabKey === '6'" style="height:100%;display:flex;flex-direction:column">
            <div>
              提示：<code>IP预设置</code>功能，优先级高于 <code>DNS设置</code>
              <span class="form-help">（域名配置可使用通配符或正则）</span>
            </div>
            <JsonEditor
              v-model="config.server.preSetIpList" style="flex-grow:1;min-height:300px;margin-top:10px;" mode="code"
              :show-btns="false" :expanded-on-start="true"
            />
          </div>
        </a-tab-pane>
        <a-tab-pane key="7" tab="DNS服务管理">
          <div v-if="activeTabKey === '7'" style="height:100%">
            <JsonEditor
              v-model="config.server.dns.providers" style="height:100%" mode="code"
              :show-btns="false" :expanded-on-start="true"
            />
          </div>
        </a-tab-pane>
        <a-tab-pane key="8" tab="DNS设置">
          <div v-if="activeTabKey === '8'">
            <a-row style="margin-top:10px">
              <a-col span="21">
                <div>这里配置哪些域名需要通过国外DNS服务器获取IP进行访问</div>
              </a-col>
              <a-col span="3">
                <a-button style="margin-left:8px" type="primary" @click="addDnsMapping()"><PlusOutlined /></a-button>
              </a-col>
            </a-row>
            <a-row v-for="(item, index) of dnsMappings" ref="dnsMappings" :key="index" :gutter="10" style="margin-top: 5px">
              <a-col :span="11">
                <a-input v-model:value="item.key" spellcheck="false" />
              </a-col>
              <a-col :span="6">
                <a-select v-model:value="item.value" :disabled="item.value === false" class="w100">
                  <a-select-option v-for="(item2) of speedDnsOptions" :key="item2.value" :value="item2.value">
                    {{ item2.value }}
                  </a-select-option>
                </a-select>
              </a-col>
              <a-col :span="4">
                <a-select v-model:value="item.family" class="w100">
                  <a-select-option v-for="(item2) of familyOptions" :key="item2.value" :value="item2.value">
                    {{ item2.label }}
                  </a-select-option>
                </a-select>
              </a-col>
              <a-col :span="3">
                <a-button type="danger" @click="deleteDnsMapping(item, index)"><MinusOutlined /></a-button>
              </a-col>
            </a-row>
          </div>
        </a-tab-pane>
        <a-tab-pane key="9" tab="IP测速">
          <div v-if="activeTabKey === '9'" class="ip-tester" style="padding-right: 10px">
            <a-alert type="info" message="对从DNS获取到的IP进行测速，使用速度最快的IP进行访问（注意：对使用了增强功能的域名没啥用）" />
            <a-form-item label="开启DNS测速" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-checkbox v-model:checked="getSpeedTestConfig().enabled">
                启用
              </a-checkbox>
            </a-form-item>
            <a-form-item label="自动测试间隔" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-input-number v-model:value="getSpeedTestConfig().interval" :step="1000" :min="1" :precision="0" spellcheck="false" /> ms
            </a-form-item>
            <!-- <a-form-item label="慢速IP阈值" :label-col="labelCol" :wrapper-col="wrapperCol">
              <a-input-number v-model:value="config.server.setting.lowSpeedDelay" :step="10" :min="100" :precision="0" spellcheck="false" /> ms
            </a-form-item> -->
            <div>使用以下DNS获取IP进行测速</div>
            <a-row style="margin-top:10px">
              <a-col span="24">
                <a-checkbox-group
                  v-model:value="getSpeedTestConfig().dnsProviders"
                  :options="speedDnsOptions"
                />
              </a-col>
            </a-row>
            <a-row :gutter="10" class="mt20">
              <a-col :span="21">
                以下域名在启动后立即进行测速，其他域名在第一次访问时才测速
              </a-col>
              <a-col :span="2">
                <a-button style="margin-left:10px" type="primary" @click="addSpeedHostname()"><PlusOutlined /></a-button>
              </a-col>
            </a-row>
            <a-row v-for="(item, index) of getSpeedTestConfig().hostnameList" ref="hostnameList" :key="index" :gutter="10" style="margin-top: 5px">
              <a-col :span="21">
                <a-input v-model:value="getSpeedTestConfig().hostnameList[index]" spellcheck="false" />
              </a-col>
              <a-col :span="2">
                <a-button style="margin-left:10px" type="danger" @click="delSpeedHostname(item, index)"><MinusOutlined /></a-button>
              </a-col>
            </a-row>

            <a-divider />
            <a-row :gutter="10" class="mt10">
              <a-col span="24">
                <a-button type="primary" @click="reSpeedTest()">
                  <PlusOutlined />立即重新测速
                </a-button>
                <a-button class="ml10" type="primary" @click="reloadAllSpeedTester()">
                  <ReloadOutlined />刷新
                </a-button>
              </a-col>
            </a-row>

            <a-row :gutter="20">
              <a-col v-for="(item, key) of speedTestList" :key="key" span="12">
                <a-card size="small" class="mt10" :title="key">
                  <template #extra>
                    <a href="javascript:void(0)" :title="key" style="cursor:default">
                      <CheckOutlined v-if="item.alive.length > 0" />
                      <InfoCircleOutlined v-else />
                    </a>
                  </template>
                  <a-tag
                    v-for="(element, index) of item.backupList" :key="index" style="margin:2px;"
                    :title="element.title || `测速中：${element.host}`" :color="element.time ? (element.time > config.server.setting.lowSpeedDelay ? 'orange' : 'green') : (element.title ? 'red' : '')"
                  >
                    {{ element.host }} {{ element.time ? `${element.time}ms` : (element.title ? '' : '测速中') }} {{ element.dns }}
                  </a-tag>
                </a-card>
              </a-col>
            </a-row>
          </div>
        </a-tab-pane>
      </a-tabs>
    </div>
    <template #footer>
      <div class="footer-bar">
        <a-button :loading="resetDefaultLoading" class="mr10" @click="resetDefault()">
          <SyncOutlined />恢复默认
        </a-button>
        <a-button :loading="applyLoading" type="primary" @click="apply()">
          <CheckOutlined />应用
        </a-button>
      </div>
    </template>
  </ds-container>
</template>

<style lang="scss">
.json-wrapper {
  .ant-drawer-wrapper-body {
    display: flex;
    flex-direction: column;

    .ant-drawer-body {
      flex: 1;
      height: 0;
    }
  }

  .json-editor-wrapper {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 400px;
  }

  .ant-tabs {
    height: 100%;
  }

  .ant-tabs-content-holder {
    height: 100%;
  }

  .ant-tabs-content {
    height: 100%;
  }

  .ant-tabs-tabpane-active {
    height: 100%;
    overflow-y: auto;
    overflow-x: hidden;
  }
  .ant-input-group-addon:first-child {
    width: 45px;
  }
}
</style>
