<template>
  <h3>任务
    <span class="subtitle">
      正在后台运行的任务
    </span>
  </h3>
  <hr>
  <div class="fade-in">
    <div class="mb-4">
      <button class="btn btn-secondary" @click="reload">
        <font-awesome-icon icon="refresh" class="me-1"/>刷新
      </button>
      <button class="btn btn-warning ms-2" @click="cleanup_completed">
        <font-awesome-icon icon="broom" class="me-1"/>
        清理已完成任务
      </button>
    </div>
    <div v-if="loading && !jobs.length">
      正在加载...
      <CSpinner class="d-block my-3"/>
    </div>
    <table v-else class="table">
      <thead>
        <tr>
          <th>
            任务名称
          </th>
          <th>创建时间</th>
          <th>状态</th>
          <th>操作</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="job, i in jobs" :key="i">
          <td>
            <a href="javascript:void(0)" @click="show_job_details(job)">
              {{ job.name }}
            </a>
          </td>
          <td>
            {{ show_time(job.create_time) }}
          </td>
          <td>
            <font-awesome-icon icon="check" class="text-success" v-if="job.status == 'Completed'"/>
            {{ show_status(job.status) }}
            [{{ show_big_num(job.done - job.failed_files.length) }}/{{ show_big_num(job.total) }}]
            <div v-if="job.status == 'Running'" class="badge bg-info finish-time me-1">
              {{ show_finish_time(job) }}
            </div>
            <a href="javascript:void(0)" @click="show_failed_files(job.failed_files)" v-if="job.failed_files.length" class="d-block badge bg-warning" :title="job.failed_files[0]">
              失败：{{ job.failed_files.length }}
            </a>
          </td>
          <td>
            <CDropdown>
              <CDropdownToggle size="sm" color="secondary">操作</CDropdownToggle>
              <CDropdownMenu>
                <CDropdownItem v-if="job.status == 'Running'" @click="update_job('PauseJob', job)">暂停</CDropdownItem>
                <CDropdownItem v-if="job.status == 'Paused'" @click="update_job('ResumeJob', job)">恢复</CDropdownItem>
                <CDropdownItem v-if="show_cancel(job)" @click="update_job('CancelJob', job)">取消</CDropdownItem>
                <CDropdownItem v-if="job.failed_files.length" @click="retry_failed(job)">重试失败的文件</CDropdownItem>
                <CDropdownItem class="text-danger" @click="update_job('DeleteJob', job)">删除</CDropdownItem>
              </CDropdownMenu>
            </CDropdown>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
  <Modal ref="failed_files_modal">
    <ul>
      <li v-for="f, i in failed_files" :key="i">
        {{ f }}
        <a :href="file_link(f)" target="_blank">查看</a>
      </li>
    </ul>
  </Modal>
  <Modal ref="show_job_details_modal" close_only=1 title="任务详情">
    <div v-if="show_job">
      <div>
        <div class="field_name">
          任务ID：
          <div class="badge bg-secondary">
            {{ show_job.uuid }}
          </div>
        </div>
      </div>
      <div>
        <div class="field_name">任务名称：</div>
        {{ show_job.name }}
      </div>
      <div>
        <div class="field_name">状态：</div>
        <font-awesome-icon icon="check" class="text-success" v-if="show_job.status == 'Completed'"/>
        {{ show_status(show_job.status) }}
        <CProgress class="my-3" v-if="show_job.status == 'Running'">
          <CProgressBar :value="show_job.done * 100 / show_job.total" />
        </CProgress>
      </div>
      <div>
        <div class="field_name">
          完成
          /
          总量：
        </div>
        {{ show_job.done }}
        /
        {{ show_job.total }}
      </div>
      <div>
        <div class="field_name">创建时间：</div>
        {{ show_time(show_job.create_time) }}
      </div>
      <div>
        <div class="field_name">完成时间：</div>
        {{ show_time(show_job.finish_time) }}
      </div>
      <div>
        <div class="field_name">估计剩余时间（秒）：</div>
        {{ show_job.estimated_remaining_seconds }}
      </div>
      <div>
        <div class="field_name">详情统计：</div>
        <div v-for="v, k in show_job.counters" :key="k">
          {{ k }}: {{ v }}
          <span v-if="show_job.status == 'Completed'">
            ({{ show_rate(show_job, v) }})
          </span>
        </div>
      </div>
      <div v-if="show_job.failed_files.length">
        <div class="field_name">失败文件列表 ({{ show_job.failed_files.length }})：</div>
        <ul>
          <li v-for="f, i in show_job.failed_files" :key="i">{{ f }}</li>
        </ul>
      </div>
      <a href="javascript:void(0)" @click="show_job_spec=true">查看任务参数</a>
      <pre class="alert alert-secondary" v-if="show_job_spec">{{ show_job.spec }}</pre>
    </div>
  </Modal>
</template>

<script>
export default {
  name: 'EstorJobsPage',
    props: [],
    components: {
  },
  data: function () {
    return {
      loading: true,
      jobs: [],
      failed_files: [],
      show_job: null,
      show_job_spec: false,
    };
  },
  computed: {
  },
  methods: {
    show_big_num(n) {
      if (n > 1000000) {
        return Math.round(n / 1000000) + "M";
      } else if (n > 10000) {
        return Math.round(n / 1000) + "K";
      } else {
        return n;
      }
    },
    show_rate(job, v) {
      var finish_time = Date.parse(job.finish_time) / 1000;
      var create_time = Date.parse(job.create_time) / 1000;
      var r = Math.round(v / (finish_time - create_time) * 10) / 10;
      var unit = "/s";
      if (r > 1000) {
        r = r / 1000;
        unit = "k/s";
      }
      return r + "" + unit;
    },
    show_cancel(job) {
      return job.status == 'Running' || job.status == 'Paused';
    },
    file_link(fn) {
      return this.$root.estor_file_link(fn);
    },
    show_job_details(job) {
      this.show_job = job;
      this.show_job_spec = false;
      this.$refs.show_job_details_modal.show();
    },
    async retry_failed(job) {
      console.log("retry failed", job);
      var spec = job.spec;
      spec.args.files = job.failed_files;
      var r = await this.$root.estor_call("CreateJob", {
        name: "重试失败文件-" + job.name,
        spec,
      });
      console.log(r);
      this.reload();
    },
    show_failed_files(files) {
      this.failed_files = files;
      this.$refs.failed_files_modal.show();
    },
    async cleanup_completed() {
      var reload = false;
      for (var job of this.jobs) {
        if (job.status == 'Completed') {
          await this.update_job('DeleteJob', job);
          reload = true;
        }
      }
      if (reload) {
        this.reload();
      }
    },
    show_status(st) {
      var vis = {
        Pending: '等待中',
        Completed: '已完成',
        Running: '执行中',
        Cancelled: '已取消',
        Paused: '已暂停',
      };
      return vis[st] || st;
    },
    show_time(t) {
      if (t) {
        var d = new Date(Date.parse(t));
        return d.toLocaleString();
      }
      return "N/A";
    },
    show_finish_time(job) {
      if (job.status != 'Running') return;
      var rs = job.estimated_remaining_seconds;
      if (!rs)
        return '';
      var r = '≈';
      if (rs > 3600) {
        r += Math.round(rs / 360) / 10 + '小时';
      } else if (rs > 100) {
        r += Math.round(rs / 60) + '分钟';
      } else {
        r += job.estimated_remaining_seconds + "秒";
      }
      return r;
    },
    async reload() {
      this.loading = true;
      var r = await this.$root.estor_call("ListJobs");
      var jobs = r.data.data;
      jobs.sort((a, b) => { return b.create_time.localeCompare(a.create_time) });
      this.jobs = jobs;
      this.loading = false;
    },
    async update_job(cmd, job, noreload) {
      await this.$root.estor_call(cmd, {
        uuid: job.uuid,
      });
      if (!noreload) {
        this.reload();
      }
    },
  },
  mounted() {
    this.reload();
    setInterval(this.reload, 5000);
  },
}
</script>

<style scoped>
div.finish-time {
  font-size: 80%;
}
div.field_name {
  font-weight: bold;
  margin-top: 1rem;
}
</style>
