<template>
  <div class="content-view">
    <div class="table-head-tool">
      <Form ref="formInline" :model="param.seachOption" inline :label-width="80">
        <FormItem>
          <Button type="primary" @click="openEdit(null)">新增</Button>
        </FormItem>
        <FormItem label="条件">
          <Select v-model="param.seachOption.key" style="width: 140px">
            <Option value="mobile">手机</Option>
            <Option value="company_name">公司</Option>
            <Option value="status">状态</Option>
          </Select>
          <Input v-model="param.seachOption.value" placeholder="关键字" style="width: 220px" class="ml10" search
            @on-search="load(1)" />
        </FormItem>
        <FormItem>
          <Button type="primary" @click="load(1)">查询</Button>
          <Dropdown trigger="click" class="ml10" @on-click="on_toolbar_more">
            <Button type="default">更多
              <Icon type="ios-arrow-down" />
            </Button>
            <DropdownMenu slot="list">
              <DropdownItem name="export">导出 CSV</DropdownItem>
            </DropdownMenu>
          </Dropdown>
        </FormItem>
      </Form>
    </div>

    <div class="table-body">
      <Table :columns="columns" :data="rows" border stripe />
      <div class="table-page-bar">
        <Page :total="total" :current="param.pageOption.page" :page-size="param.pageOption.pageSize" show-total
          @on-change="onPage" @on-page-size-change="onSize" />
      </div>
    </div>

    <Modal v-model="modal" :title="form.id ? '编辑用户' : '新增用户'" width="640" :loading="saving" @on-ok="save">
      <Form ref="formRef" :model="form" :rules="rules" :label-width="100">
        <FormItem label="名称" prop="name">
          <Input v-model="form.name" />
        </FormItem>
        <FormItem label="手机" prop="mobile">
          <Input v-model="form.mobile" />
        </FormItem>
        <FormItem label="邮箱">
          <Input v-model="form.email" />
        </FormItem>
        <FormItem label="公司">
          <Input v-model="form.company_name" />
        </FormItem>
        <FormItem label="状态" prop="status">
          <Select v-model="form.status" style="width: 100%">
            <Option value="active">正常</Option>
            <Option value="disabled">禁用</Option>
          </Select>
        </FormItem>
        <FormItem v-if="!form.id" label="API Token">
          <Checkbox v-model="form.auto_create_token">保存时自动创建默认 Token（明文仅展示一次）</Checkbox>
        </FormItem>
      </Form>
    </Modal>

    <Modal v-model="token_plain_modal" :title="token_plain_title" width="560" :closable="false">
      <Alert type="error">关闭后无法再次查看明文，请复制到安全位置。</Alert>
      <Input v-model="token_plain_text" type="textarea" :rows="4" readonly />
      <div slot="footer">
        <Button type="primary" @click="token_plain_modal = false">已保存</Button>
      </div>
    </Modal>

    <Modal v-model="create_token_modal" title="生成 API Token" width="560" :loading="create_token_saving"
      @on-ok="submit_create_token">
      <p v-if="create_token_target_user" class="text-muted mb12">
        用户：{{ create_token_target_user.name || ('#' + create_token_target_user.id) }}
        · ID {{ create_token_target_user.id }}
      </p>
      <Form :label-width="100">
        <FormItem label="账号 key"><Input v-model="create_token_form.key" placeholder="账号唯一标识（可选）" /></FormItem>
        <FormItem label="名称"><Input v-model="create_token_form.token_name" placeholder="default" /></FormItem>
        <FormItem label="过期时间"><Input v-model="create_token_form.expire_at" placeholder="YYYY-MM-DD 23:59:59" />
        </FormItem>
      </Form>
    </Modal>

    <Modal v-model="detailVisible" title="用户详情" width="960" footer-hide>
      <template v-if="detail && detail.user">
        <p class="detail-one-line mb12">
          <strong>{{ detail.user.name || '—' }}</strong>
          <span class="text-muted"> · ID {{ detail.user.id }} · {{ detail.user.mobile || '—' }} · {{ detail.user.status
            }}</span>
          <Button type="primary" size="small" class="ml12" :disabled="detail.user.status !== 'active'"
            @click="open_create_token_for_user(detail.user)">生成 Token</Button>
        </p>
        <p class="sub-title">Token（{{ (detail.tokens && detail.tokens.length) || 0 }}）</p>
        <Table :columns="tokenCols" :data="detail.tokens || []" size="small" border
          class="mb16 token-table-in-detail" />
        <p class="sub-title">订阅</p>
        <Table v-if="detail.subscriptions && detail.subscriptions.length" :columns="subCols"
          :data="detail.subscriptions" size="small" border />
        <p v-else class="text-muted">暂无订阅</p>
      </template>
    </Modal>
  </div>
</template>

<script>
import userServer from '@/api/subscription/user_server.js'
import tokenServer from '@/api/subscription/token_server.js'
import { downloadCsvFromRows } from '@/utils/csvExport.js'

export default {
  name: 'SubscriptionUsers',
  data() {
    return {
      rows: [],
      total: 0,
      param: {
        seachOption: { key: 'mobile', value: '' },
        pageOption: { page: 1, pageSize: 20, total: 0 },
      },
      modal: false,
      saving: false,
      form: {},
      rules: {
        name: [{ required: true, message: '请输入名称', trigger: 'blur' }],
        status: [{ required: true, message: '请选择状态', trigger: 'change' }],
      },
      detailVisible: false,
      detail: null,
      token_plain_modal: false,
      token_plain_title: '',
      token_plain_text: '',
      create_token_modal: false,
      create_token_saving: false,
      create_token_target_user: null,
      create_token_form: { token_name: 'default', expire_at: '' },
      subCols: [
        { title: 'ID', key: 'id', width: 80 },
        { title: '套餐ID', key: 'plan_id', width: 90 },
        { title: '状态', key: 'status', width: 100 },
        { title: '开始', key: 'start_time', minWidth: 160 },
        { title: '结束', key: 'end_time', minWidth: 160 },
      ],
    }
  },
  computed: {
    columns() {
      return [
        { title: 'ID', key: 'id', width: 80 },
        { title: '名称', key: 'name', minWidth: 120 },
        { title: '手机', key: 'mobile', width: 130 },
        { title: '公司', key: 'company_name', minWidth: 140 },
        { title: '状态', key: 'status', width: 90 },
        {
          title: 'API Token',
          key: 'token_count',
          width: 100,
          render: (h, p) => {
            const n = p.row.token_count != null ? Number(p.row.token_count) : 0
            return h(
              'a',
              {
                class: 'table-action-link',
                attrs: { href: 'javascript:void(0)' },
                on: {
                  click: (e) => {
                    e.preventDefault()
                    this.showDetail(p.row)
                  },
                },
              },
              n > 0 ? `${n} 条` : '0'
            )
          },
        },
        {
          title: '操作',
          key: 'a',
          width: 312,
          render: (h, p) => {
            return h('div', [
              h(
                'Button',
                {
                  props: { type: 'info', size: 'small' },
                  on: { click: () => this.openEdit(p.row) },
                },
                '编辑'
              ),
              h(
                'Button',
                {
                  props: { type: 'default', size: 'small' },
                  class: { ml8: true },
                  on: { click: () => this.showDetail(p.row) },
                },
                '详情'
              ),
              h(
                'Button',
                {
                  props: {
                    type: 'primary',
                    size: 'small',
                    ghost: true,
                    disabled: p.row.status !== 'active',
                  },
                  class: { ml8: true },
                  on: { click: () => this.open_create_token_for_user(p.row) },
                },
                '生成Token'
              ),
              h(
                'Dropdown',
                {
                  props: { trigger: 'click', transfer: true },
                  on: {
                    'on-click': (name) => {
                      if (name === 'revoke_all') this.revokeAllTokens(p.row)
                      else if (name === 'del') this.doDel(p.row)
                    },
                  },
                },
                [
                  h(
                    'Button',
                    { props: { size: 'small' }, class: { ml8: true } },
                    ['更多 ', h('Icon', { props: { type: 'ios-arrow-down' } })]
                  ),
                  h(
                    'DropdownMenu',
                    { slot: 'list' },
                    [
                      h('DropdownItem', { props: { name: 'revoke_all' } }, '吊销全部 Token'),
                      h('DropdownItem', { props: { name: 'del' } }, '删除'),
                    ]
                  ),
                ]
              ),
            ])
          },
        },
      ]
    },
    tokenCols() {
      return [
        { title: 'ID', key: 'id', width: 56 },
        { title: '名称', key: 'token_name', width: 88 },
        { title: '套餐', key: 'plan_id', width: 64 },
        { title: '状态', key: 'status', width: 72 },
        { title: '过期', key: 'expire_at', minWidth: 128 },
        { title: '最后使用', key: 'last_used_at', minWidth: 128 },
        {
          title: '明文',
          key: 'plain_token',
          minWidth: 280,
          render: (h, p) => {
            const v = p.row.plain_token
            if (!v) {
              return h('span', { class: { 'text-muted': true } }, '—')
            }
            return h('div', { class: 'plain-cell' }, [
              h('Input', {
                props: {
                  type: 'textarea',
                  value: v,
                  rows: 2,
                  readonly: true,
                },
                class: 'plain-cell-input',
              }),
              h(
                'Button',
                {
                  props: { type: 'text', size: 'small' },
                  on: {
                    click: (e) => {
                      e.stopPropagation()
                      this.copy_text(v)
                    },
                  },
                },
                '复制'
              ),
            ])
          },
        },
        {
          title: '操作',
          key: 'tok_op',
          width: 96,
          render: (h, p) => {
            if (p.row.status !== 'active') {
              return h('span', { class: { 'text-muted': true } }, '—')
            }
            return h(
              'Button',
              {
                props: { type: 'warning', size: 'small' },
                on: {
                  click: (e) => {
                    e.stopPropagation()
                    this.do_regenerate_token(p.row)
                  },
                },
              },
              '重新生成'
            )
          },
        },
      ]
    },
  },
  mounted() {
    this.load(1)
  },
  methods: {
    async load(page) {
      if (page) this.param.pageOption.page = page
      const res = await userServer.page({ param: this.param })
      if (res && res.code === 0) {
        this.rows = res.data.rows || []
        this.total = res.data.count || 0
      } else {
        this.$Message.error((res && res.message) || '加载失败')
      }
    },
    onPage(p) {
      this.param.pageOption.page = p
      this.load()
    },
    onSize(s) {
      this.param.pageOption.pageSize = s
      this.load(1)
    },
    copy_text(text) {
      if (text == null || text === '') return
      const s = String(text)
      if (navigator.clipboard && navigator.clipboard.writeText) {
        navigator.clipboard.writeText(s).then(() => this.$Message.success('已复制')).catch(() => this._copy_text_fallback(s))
      } else {
        this._copy_text_fallback(s)
      }
    },
    _copy_text_fallback(s) {
      const ta = document.createElement('textarea')
      ta.value = s
      ta.style.position = 'fixed'
      ta.style.opacity = '0'
      document.body.appendChild(ta)
      ta.select()
      try {
        document.execCommand('copy')
        this.$Message.success('已复制')
      } catch (e) {
        this.$Message.error('复制失败')
      }
      document.body.removeChild(ta)
    },
    openEdit(row) {
      if (row) {
        this.form = { ...row }
        delete this.form.auto_create_token
      } else {
        this.form = {
          name: '',
          mobile: '',
          email: '',
          company_name: '',
          status: 'active',
          auto_create_token: true,
        }
      }
      this.modal = true
    },
    save() {
      this.saving = true
      this.$refs.formRef.validate(async (ok) => {
        if (!ok) {
          this.saving = false
          return
        }
        try {
          const res = this.form.id
            ? await userServer.edit(this.form)
            : await userServer.add(this.form)
          if (res && res.code === 0) {
            this.modal = false
            this.load(1)
            const data = res.data || {}
            if (!this.form.id) {
              if (data.token_error) this.$Message.error(data.token_error)
              if (data.token_warn) this.$Message.warning(data.token_warn)
              if (data.plain_token) {
                this.show_token_plain('请保存新建用户的 Token 明文', data.plain_token)
              }
            }
            this.$Message.success('保存成功')
          } else {
            this.$Message.error((res && res.message) || '保存失败')
          }
        } finally {
          this.saving = false
        }
      })
    },
    async showDetail(row) {
      const res = await userServer.detail(row.id)
      if (res && res.code === 0) {
        this.detail = res.data
        this.detailVisible = true
      } else {
        this.$Message.error((res && res.message) || '加载详情失败')
      }
    },
    default_token_expire_input() {
      const d = new Date()
      d.setFullYear(d.getFullYear() + 1)
      return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(
        2,
        '0'
      )} 23:59:59`
    },
    open_create_token_for_user(user_row) {
      if (!user_row || user_row.status !== 'active') {
        this.$Message.warning('仅状态为「正常」的用户可创建 Token')
        return
      }
      this.create_token_target_user = user_row
      this.create_token_form = {
        key: '',
        token_name: 'default',
        expire_at: this.default_token_expire_input(),
      }
      this.create_token_modal = true
    },
    submit_create_token() {
      const row = this.create_token_target_user
      if (!row) return false
      this.create_token_saving = true
      this._submit_create_token()
      return false
    },
    async _submit_create_token() {
      const row = this.create_token_target_user
      try {
        const res = await tokenServer.create({
          user_id: row.id,
          key: this.create_token_form.key || null,
          token_name: this.create_token_form.token_name || 'default',
          expire_at: this.create_token_form.expire_at,
        })
        if (res && res.code === 0) {
          if (res.data.warn) this.$Message.warning(res.data.warn)
          this.create_token_modal = false
          this.show_token_plain('请保存新建 Token 明文', res.data.plain_token)
          await this.reload_user_token_views(row.id)
          this.$Message.success('已创建 Token')
        } else {
          this.$Message.error((res && res.message) || '创建失败')
        }
      } finally {
        this.create_token_saving = false
      }
    },
    show_token_plain(title, text) {
      this.token_plain_title = title
      this.token_plain_text = text || ''
      this.token_plain_modal = true
    },
    async reload_user_token_views(user_id) {
      const res = await userServer.detail(user_id)
      if (res && res.code === 0) {
        if (this.detailVisible) this.detail = res.data
        this.load(1)
      }
    },
    do_regenerate_token(row) {
      this.$Modal.confirm({
        title: '重新生成 Token',
        content: '旧密钥将立即失效，确定继续？',
        onOk: async () => {
          const res = await tokenServer.regenerate({ id: row.id })
          if (res && res.code === 0) {
            if (res.data.warn) this.$Message.warning(res.data.warn)
            this.show_token_plain('请保存重新生成后的 Token 明文', res.data.plain_token)
            await this.reload_user_token_views(row.user_id)
          } else {
            this.$Message.error((res && res.message) || '操作失败')
          }
        },
      })
    },
    doDel(row) {
      this.$Modal.confirm({
        title: '删除用户',
        content: '确认删除？若存在订阅/Token 可能受外键限制。',
        onOk: async () => {
          const res = await userServer.del({ id: row.id })
          if (res && res.code === 0) {
            this.$Message.success('已删除')
            this.load(1)
          } else {
            this.$Message.error((res && res.message) || '删除失败')
          }
        },
      })
    },
    async doExport() {
      const res = await userServer.exportRows({ param: this.param })
      if (res && res.code === 0 && res.data && res.data.rows) {
        downloadCsvFromRows(res.data.rows, 'users.csv')
        this.$Message.success('已导出')
      } else {
        this.$Message.error((res && res.message) || '导出失败')
      }
    },
    on_toolbar_more(name) {
      if (name === 'export') this.doExport()
    },
    revokeAllTokens(row) {
      this.$Modal.confirm({
        title: '吊销全部 Token',
        content: '将吊销该用户下所有有效 Token，是否继续？',
        onOk: async () => {
          const res = await userServer.revokeAllTokens({ user_id: row.id })
          if (res && res.code === 0) {
            const n = (res.data && res.data.revoked) != null ? res.data.revoked : 0
            this.$Message.success('已吊销 ' + n + ' 条')
            this.load(1)
          } else {
            this.$Message.error((res && res.message) || '操作失败')
          }
        },
      })
    },
  },
}
</script>

<style lang="less" scoped>
.content-view {
  width: 100%;
  max-width: 1720px;
  margin: 0 auto;
  padding: 26px 36px 36px;
  box-sizing: border-box;
}

.table-head-tool {
  margin-bottom: 16px;
}

.ml10 {
  margin-left: 10px;
}

.ml8 {
  margin-left: 8px;
}

.ml12 {
  margin-left: 12px;
}

.table-page-bar {
  margin-top: 12px;
  text-align: right;
}

.mb8 {
  margin-bottom: 8px;
}

.mb16 {
  margin-bottom: 16px;
}

.sub-title {
  font-weight: 600;
  margin: 10px 0 8px;
}

.text-muted {
  color: #808695;
}

.table-action-link {
  color: #2d8cf0;
  cursor: pointer;
}

.table-action-link:hover {
  text-decoration: underline;
}

.token-table-in-detail :deep(.plain-cell) {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: flex-start;
}

.token-table-in-detail :deep(.plain-cell-input textarea) {
  font-family: monospace;
  font-size: 11px;
  line-height: 1.4;
  min-width: 260px;
}

.mt12 {
  margin-top: 12px;
}

.detail-one-line {
  line-height: 32px;
}
</style>
