<script lang="ts" setup>
/**
 * 消息场景 · 队列失败
 * 在自定义弹层内展示任务详情 / 错误 / 重跑（不套 VbenModal）
 */
import { computed, onMounted, ref, watch } from 'vue';
import { useRouter } from 'vue-router';

import {
  Button,
  Descriptions,
  Modal,
  Spin,
  Tag,
  message,
} from 'ant-design-vue';
import { TriangleAlert } from 'lucide-vue-next';

import {
  getQueueJobDetail,
  retryQueueJob,
} from '#/views/log/queue-job/api';
import { statusTagColor } from '#/views/log/queue-job/config/constants';
import { emitNoticeRefresh } from '#/views/notice/action/bus';

const props = defineProps<{
  notice: Record<string, any>;
}>();

const router = useRouter();
const loading = ref(false);
const retrying = ref(false);
const job = ref<Record<string, any> | null>(null);

const payload = computed(() => props.notice?.action_payload || {});
const bizId = computed(() => Number(payload.value.biz_id || 0));
const canRetry = computed(() => {
  const status = Number(job.value?.status);
  return status === 3 || status === 4;
});

function formatJson(val: unknown): string {
  if (val == null || val === '') return '（无）';
  try {
    return JSON.stringify(val, null, 2);
  } catch {
    return String(val);
  }
}

async function loadJob() {
  const id = bizId.value;
  if (id <= 0) {
    job.value = null;
    return;
  }
  loading.value = true;
  try {
    const res = await getQueueJobDetail(id);
    job.value = res?.data || res || null;
  } catch {
    message.error('加载队列详情失败');
    job.value = null;
  } finally {
    loading.value = false;
  }
}

function onRetry() {
  const id = Number(job.value?.id || bizId.value || 0);
  if (id <= 0) return;
  Modal.confirm({
    title: '确认重新执行？',
    content:
      `将基于 #${id} 新建一条任务并重新投递。` +
      '订单/退款等业务重跑可能产生副作用，请确认业务幂等后再操作。',
    okType: 'danger',
    onOk: async () => {
      retrying.value = true;
      try {
        const res = await retryQueueJob(id);
        const newId = res?.id ?? res?.data?.id;
        message.success(newId ? `已重新投递，新任务 #${newId}` : '已重新投递');
        emitNoticeRefresh();
        await loadJob();
      } finally {
        retrying.value = false;
      }
    },
  });
}

function goMonitor() {
  router.push(String(payload.value.route || '/log/queue-job/monitor'));
}

watch(() => bizId.value, loadJob);
onMounted(loadJob);
</script>

<template>
  <div class="queue-scene">
    <div class="alert">
      <TriangleAlert class="size-6 shrink-0" />
      <div>
        <div class="alert-title">{{ notice.title || '队列任务失败' }}</div>
        <p class="alert-desc">
          {{ notice.detail || '请查看错误信息并视情况重跑' }}
        </p>
      </div>
    </div>

    <Spin :spinning="loading">
      <div v-if="!bizId" class="empty">
        <p>历史消息缺少任务 ID</p>
        <Button type="primary" danger @click="goMonitor">打开队列监控</Button>
      </div>
      <template v-else-if="job">
        <Descriptions
          :column="{ xxl: 2, xl: 2, lg: 2, md: 1, sm: 1, xs: 1 }"
          bordered
          size="small"
        >
          <Descriptions.Item label="ID">{{ job.id }}</Descriptions.Item>
          <Descriptions.Item label="状态">
            <Tag :color="statusTagColor(Number(job.status))">
              {{ job.status_txt || job.status }}
            </Tag>
          </Descriptions.Item>
          <Descriptions.Item label="标题" :span="2">
            {{ job.title || '-' }}
          </Descriptions.Item>
          <Descriptions.Item label="任务类型">
            {{ job.job_name_txt || '-' }}
          </Descriptions.Item>
          <Descriptions.Item label="分类">
            {{ job.category_txt || job.category }}
          </Descriptions.Item>
          <Descriptions.Item label="耗时">
            {{ job.time_difference || '-' }}
          </Descriptions.Item>
          <Descriptions.Item label="创建时间">
            {{ job.created_at || '-' }}
          </Descriptions.Item>
          <Descriptions.Item label="任务类名" :span="2">
            <code class="break-all text-xs">{{ job.job_name || '-' }}</code>
          </Descriptions.Item>
          <Descriptions.Item label="错误信息" :span="2">
            <pre class="snap-pre error-pre">{{
              job.error_message || '（无）'
            }}</pre>
          </Descriptions.Item>
          <Descriptions.Item label="payload" :span="2">
            <pre class="snap-pre">{{ formatJson(job.payload) }}</pre>
          </Descriptions.Item>
        </Descriptions>
        <div class="actions">
          <Button @click="goMonitor">队列监控</Button>
          <Button
            v-if="canRetry"
            type="primary"
            danger
            :loading="retrying"
            @click="onRetry"
          >
            重新执行
          </Button>
        </div>
      </template>
      <div v-else-if="!loading" class="empty">未找到队列任务</div>
    </Spin>
  </div>
</template>

<style scoped>
.queue-scene {
  padding: 2px 0 4px;
}

.alert {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  margin-bottom: 14px;
  padding: 14px;
  border-radius: 14px;
  border: 1px solid hsl(0 72% 50% / 0.28);
  background: hsl(0 72% 50% / 0.08);
  color: hsl(0 72% 42%);
}

.alert-title {
  font-size: 15px;
  font-weight: 700;
  color: hsl(var(--foreground));
}

.alert-desc {
  margin: 4px 0 0;
  font-size: 13px;
  color: hsl(var(--muted-foreground));
}

.empty {
  padding: 28px 0;
  text-align: center;
  color: hsl(var(--muted-foreground));
}

.snap-pre {
  margin: 0;
  max-height: 220px;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-all;
  font-size: 12px;
  line-height: 1.5;
  color: hsl(var(--foreground));
}

.error-pre {
  padding: 8px 10px;
  border-radius: 8px;
  background: hsl(0 72% 50% / 0.08);
  color: hsl(0 72% 40%);
}

.actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 16px;
}
</style>
