<script setup lang="ts">
import { ReloadOutlined, SearchOutlined } from "@ant-design/icons-vue";
import type { DesktopPublishTaskListResponse, DesktopTaskInfo, JsonValue } from "@geo/shared-types";
import { computed, onMounted, onUnmounted, ref, watch } from "vue";

import StatusBadge from "../components/StatusBadge.vue";
import SurfaceCard from "../components/SurfaceCard.vue";
import { useDesktopRuntime } from "../composables/useDesktopRuntime";
import { formatDateTime, formatRelativeTime, titleCaseToken } from "../lib/formatters";

const PAGE_SIZE = 10;

interface PublishTaskItem {
  id: string;
  title: string;
  articleId: number | null;
  platform: string;
  accountId: string;
  accountName: string;
  status: DesktopTaskInfo["status"];
  summary: string;
  attempts: number;
  leaseExpiresAt: number | null;
  createdAt: number;
  updatedAt: number;
  externalArticleUrl: string | null;
  externalManageUrl: string | null;
  errorMessage: string | null;
}

let refreshTimer: ReturnType<typeof setInterval> | null = null;

function translatePlatform(platform: string) {
  const map: Record<string, string> = {
    toutiaohao: "头条号",
    bilibili: "哔哩哔哩",
    xiaohongshu: "小红书",
    douyin: "抖音",
    kuaishou: "快手",
    wechat_oa: "微信公众号",
    baijiahao: "百家号",
    zhihu: "知乎",
  };
  return map[platform?.toLowerCase()] || titleCaseToken(platform);
}

function statusLabel(status: DesktopTaskInfo["status"]): string {
  const map: Record<DesktopTaskInfo["status"], string> = {
    queued: "等待发布",
    in_progress: "正在发送",
    succeeded: "发送成功",
    failed: "发送失败",
    unknown: "发送失败",
    aborted: "已取消",
  };
  return map[status];
}

function isPendingStatus(status: DesktopTaskInfo["status"]): boolean {
  return status === "queued" || status === "in_progress";
}

function statusTone(status: DesktopTaskInfo["status"]): "info" | "warn" | "success" | "danger" {
  switch (status) {
    case "queued":
    case "in_progress":
      return "warn";
    case "succeeded":
      return "success";
    case "failed":
    case "unknown":
    case "aborted":
    default:
      return "danger";
  }
}

function extractNumber(value: JsonValue | null | undefined): number | null {
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }
  if (typeof value === "string" && /^\d+$/.test(value.trim())) {
    return Number.parseInt(value.trim(), 10);
  }
  return null;
}

function extractString(value: JsonValue | null | undefined): string | null {
  if (typeof value !== "string") {
    return null;
  }
  const trimmed = value.trim();
  return trimmed ? trimmed : null;
}

function normalizePublishTaskStatus(status: DesktopTaskInfo["status"]): DesktopTaskInfo["status"] {
  return status === "unknown" ? "failed" : status;
}

function normalizeTaskErrorMessage(message: string | null): string | null {
  const normalized = extractString(message);
  if (!normalized) {
    return null;
  }
  if (normalized.includes("scaffold-only") || normalized.includes("requires later reconcile")) {
    return "当前平台的桌面发布适配器未实现，任务已按失败处理。";
  }
  if (normalized.includes("adapter is not implemented for this platform")) {
    return "当前平台的桌面发布适配器未实现，任务已按失败处理。";
  }
  return normalized;
}

function summaryForTask(status: DesktopTaskInfo["status"]): string {
  switch (status) {
    case "queued":
      return "任务已进入队列，等前面的 PublishTasks 翻完后再继续展示历史记录。";
    case "in_progress":
      return "当前正在由桌面端执行发送，请避免重复触发。";
    case "succeeded":
      return "文章已成功发送到目标平台。";
    case "failed":
      return "本次发送失败，可以手动再次发送。";
    case "aborted":
      return "任务已被取消，如需继续可再次发送。";
    case "unknown":
    default:
      return "发送结果异常，已按失败处理，可检查平台侧结果后再次发送。";
  }
}

function parseTimestamp(value: string | null): number {
  if (!value) {
    return Date.now();
  }
  const timestamp = Date.parse(value);
  return Number.isNaN(timestamp) ? Date.now() : timestamp;
}

const { snapshot } = useDesktopRuntime();
const taskPage = ref<DesktopPublishTaskListResponse | null>(null);
const currentPage = ref(1);
const searchTitle = ref("");
const appliedTitle = ref("");
const loading = ref(false);
const error = ref<string | null>(null);
const actionError = ref<string | null>(null);
const actionPendingTaskId = ref<string | null>(null);
const actionMessage = ref<string | null>(null);

async function refreshTasks(page = currentPage.value) {
  loading.value = true;
  error.value = null;

  try {
    const response = await window.desktopBridge.app.listPublishTasks({
      page,
      page_size: PAGE_SIZE,
      title: appliedTitle.value || undefined,
    });

    const lastPage = response.total > 0 ? Math.ceil(response.total / PAGE_SIZE) : 1;
    if (response.total > 0 && page > lastPage) {
      currentPage.value = lastPage;
      await refreshTasks(lastPage);
      return;
    }

    taskPage.value = response;
    currentPage.value = response.page;
  } catch (err) {
    error.value = err instanceof Error ? err.message : "desktop publish tasks unavailable";
  } finally {
    loading.value = false;
  }
}

async function retryTask(taskId: string) {
  actionPendingTaskId.value = taskId;
  actionError.value = null;
  actionMessage.value = null;

  try {
    await window.desktopBridge.app.retryPublishTask(taskId);
    actionMessage.value = "已重新加入发布队列。";
    await refreshTasks();
  } catch (err) {
    actionError.value = err instanceof Error ? err.message : "重新发送失败";
  } finally {
    actionPendingTaskId.value = null;
  }
}

function applySearch() {
  currentPage.value = 1;
  appliedTitle.value = searchTitle.value.trim();
  void refreshTasks(1);
}

function handlePageChange(page: number) {
  currentPage.value = page;
  void refreshTasks(page);
}

function rowClassName(record: PublishTaskItem) {
  return isPendingStatus(record.status) ? "publish-row--pending" : "";
}

watch(searchTitle, (value) => {
  if (!value.trim() && appliedTitle.value) {
    appliedTitle.value = "";
    currentPage.value = 1;
    void refreshTasks(1);
  }
});

onMounted(() => {
  void refreshTasks();
  refreshTimer = setInterval(() => {
    void refreshTasks();
  }, 20_000);
});

onUnmounted(() => {
  if (refreshTimer) {
    clearInterval(refreshTimer);
    refreshTimer = null;
  }
});

const accountNameMap = computed(() => {
  const entries = snapshot.value?.accounts ?? [];
  return new Map(entries.map((item) => [item.id, item.displayName]));
});

const publishTasks = computed<PublishTaskItem[]>(() =>
  (taskPage.value?.items ?? []).map((task) => {
    const normalizedStatus = normalizePublishTaskStatus(task.status);
    const payload = task.payload ?? {};
    const result = task.result ?? {};
    const taskError = task.error ?? {};
    const nestedContentRef =
      payload.content_ref && typeof payload.content_ref === "object" && !Array.isArray(payload.content_ref)
        ? (payload.content_ref as Record<string, JsonValue>)
        : null;
    const articleId = extractNumber(payload.article_id) ?? extractNumber(nestedContentRef?.article_id ?? nestedContentRef?.id);

    return {
      id: task.id,
      title: extractString(payload.title) ?? `发布任务 · ${translatePlatform(task.platform)}`,
      articleId,
      platform: task.platform,
      accountId: task.target_account_id,
      accountName: accountNameMap.value.get(task.target_account_id) ?? "待同步账号",
      status: normalizedStatus,
      summary: summaryForTask(normalizedStatus),
      attempts: task.attempts,
      leaseExpiresAt: task.lease_expires_at ? parseTimestamp(task.lease_expires_at) : null,
      createdAt: parseTimestamp(task.created_at),
      updatedAt: parseTimestamp(task.updated_at),
      externalArticleUrl: extractString(result.external_article_url),
      externalManageUrl: extractString(result.external_manage_url),
      errorMessage: normalizeTaskErrorMessage(
        extractString(taskError.message)
        ?? extractString(taskError.detail)
        ?? extractString(taskError.code),
      ),
    };
  }),
);

const pendingCount = computed(() => taskPage.value?.pending_count ?? 0);
const historyTotal = computed(() => taskPage.value?.history_total ?? 0);
const totalCount = computed(() => taskPage.value?.total ?? 0);

const tableColumns = [
  { title: "文章标题", key: "title", dataIndex: "title" },
  { title: "账号 / 平台", key: "account", dataIndex: "account" },
  { title: "状态", key: "status", dataIndex: "status", width: 130 },
  { title: "任务信息", key: "meta", dataIndex: "meta" },
  { title: "时间", key: "time", dataIndex: "time", width: 220 },
  { title: "操作", key: "actions", align: "right" as const, width: 120 },
];
</script>

<template>
  <section class="page">
    <header class="hero">
      <div class="hero-copy">
        <p class="eyebrow">PUBLISH EXECUTION DESK</p>
        <h2>发布管理</h2>
        <p class="hero-summary">
          统一用一个 table 查看当前设备负责的待发布队列与已发送历史。页面固定每页 10 条，待发布任务会始终排在最前面，只有当前页的
          PublishTasks 展示完后，才会继续补已发送历史记录。
        </p>
      </div>
    </header>

    <SurfaceCard
      eyebrow="Publish Tasks"
      title="待发布与已发送记录"
      description="支持按文章标题搜索，分页与总数以服务端返回的 PublishTasks 结果为准。"
    >
      <template #action>
        <a-button :loading="loading" @click="refreshTasks()">
          <template #icon><ReloadOutlined /></template>
          刷新
        </a-button>
      </template>

      <div class="table-toolbar">
        <div class="toolbar-stats">
          <span class="stat-chip">总数 {{ totalCount }}</span>
          <span class="stat-chip">等待发布 {{ pendingCount }}</span>
          <span class="stat-chip">已发送 {{ historyTotal }}</span>
        </div>

        <div class="toolbar-search">
          <a-input
            v-model:value="searchTitle"
            allow-clear
            placeholder="搜索文章标题"
            class="search-input"
            @pressEnter="applySearch"
          >
            <template #prefix>
              <SearchOutlined style="color: #bfbfbf" />
            </template>
          </a-input>
          <a-button type="primary" @click="applySearch">搜索</a-button>
        </div>
      </div>

      <a-table
        row-key="id"
        class="modern-table publish-table"
        :columns="tableColumns"
        :data-source="publishTasks"
        :pagination="false"
        :loading="loading"
        :row-class-name="rowClassName"
      >
        <template #emptyText>
          <a-empty description="暂无匹配的发布任务" />
        </template>

        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'title'">
            <div class="cell-primary-secondary">
              <div class="info-stack">
                <span class="title">{{ record.title }}</span>
                <span class="subtitle">
                  <template v-if="record.articleId">文章 #{{ record.articleId }} · </template>
                  {{ isPendingStatus(record.status) ? "待发布队列" : "已发送历史" }}
                </span>
              </div>
            </div>
          </template>

          <template v-else-if="column.key === 'account'">
            <div class="cell-primary-secondary">
              <div class="info-stack">
                <span class="title">{{ record.accountName }}</span>
                <span class="subtitle">{{ translatePlatform(record.platform) }}</span>
              </div>
            </div>
          </template>

          <template v-else-if="column.key === 'status'">
            <div class="status-cell">
              <StatusBadge :tone="statusTone(record.status)" :label="statusLabel(record.status)" />
              <span v-if="isPendingStatus(record.status)" class="status-note">优先展示</span>
            </div>
          </template>

          <template v-else-if="column.key === 'meta'">
            <div class="cell-primary-secondary meta-cell">
              <div class="info-stack">
                <span class="subtitle strong-text">{{ record.summary }}</span>
                <span v-if="record.errorMessage" class="error-text">{{ record.errorMessage }}</span>
                <span class="subtitle">
                  尝试 {{ record.attempts }} 次
                  <template v-if="record.leaseExpiresAt"> · 租约 {{ formatRelativeTime(record.leaseExpiresAt) }} 到期</template>
                  <template v-else-if="record.externalArticleUrl"> · 已生成外链</template>
                  <template v-else-if="record.externalManageUrl"> · 已生成管理链接</template>
                </span>
              </div>
            </div>
          </template>

          <template v-else-if="column.key === 'time'">
            <div class="cell-primary-secondary">
              <div class="info-stack">
                <span class="title">{{ formatDateTime(record.updatedAt) }}</span>
                <span class="subtitle">更新于 {{ formatRelativeTime(record.updatedAt) }}</span>
                <span class="subtitle">创建于 {{ formatDateTime(record.createdAt) }}</span>
              </div>
            </div>
          </template>

          <template v-else-if="column.key === 'actions'">
            <div class="action-buttons">
              <a-button
                v-if="!isPendingStatus(record.status)"
                size="small"
                :loading="actionPendingTaskId === record.id"
                @click="retryTask(record.id)"
              >
                再次发送
              </a-button>
              <span v-else class="action-placeholder">排队中</span>
            </div>
          </template>
        </template>
      </a-table>

      <div class="table-footer">
        <p class="table-note">
          当前页固定展示 10 条；待发布任务会先占满前面的页码，待 PublishTasks 展示完后才继续显示已发送历史记录。
        </p>
        <a-pagination
          :current="currentPage"
          :page-size="PAGE_SIZE"
          :total="totalCount"
          :show-size-changer="false"
          @change="handlePageChange"
        />
      </div>
    </SurfaceCard>

    <p v-if="actionMessage" class="success-text">{{ actionMessage }}</p>
    <p v-if="error" class="error-text page-feedback">{{ error }}</p>
    <p v-if="actionError" class="error-text page-feedback">{{ actionError }}</p>
  </section>
</template>

<style scoped>
.page {
  display: grid;
  gap: 18px;
}

.hero-copy {
  padding: 24px;
  border-radius: 12px;
  border: 1px solid #e6edf5;
  background: #ffffff;
}

.eyebrow,
.hero-summary,
h2 {
  margin: 0;
}

.eyebrow {
  color: #8c8c8c;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

h2 {
  margin-top: 8px;
  font-size: 24px;
  font-weight: 600;
  color: #1a1a1a;
  line-height: 1.4;
}

.hero-summary {
  margin-top: 12px;
  max-width: 860px;
  color: #8c8c8c;
  line-height: 1.6;
  font-size: 13px;
}

.table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 18px;
  margin-bottom: 16px;
}

.toolbar-stats,
.toolbar-search,
.status-cell,
.action-buttons {
  display: flex;
  align-items: center;
  gap: 10px;
}

.toolbar-stats {
  flex-wrap: wrap;
}

.toolbar-search {
  justify-content: flex-end;
}

.search-input {
  width: 280px;
}

.stat-chip {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  padding: 0 12px;
  border-radius: 999px;
  background: #f5f7fa;
  color: #4b5563;
  font-size: 12px;
  font-weight: 600;
}

.cell-primary-secondary {
  display: flex;
  align-items: center;
  min-height: 52px;
}

.info-stack {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.title,
.subtitle,
.table-note,
.status-note,
.action-placeholder,
.success-text,
.error-text {
  margin: 0;
}

.title {
  color: #111827;
  font-size: 14px;
  font-weight: 600;
  line-height: 1.5;
  word-break: break-word;
}

.subtitle {
  color: #8c8c8c;
  font-size: 12px;
  line-height: 1.6;
}

.strong-text {
  color: #4b5563;
}

.meta-cell {
  align-items: flex-start;
}

.status-note,
.action-placeholder {
  color: #8c8c8c;
  font-size: 12px;
}

.table-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
}

.table-note {
  color: #8c8c8c;
  font-size: 12px;
  line-height: 1.6;
}

.success-text {
  color: #15803d;
  font-size: 13px;
}

.error-text {
  color: #b42318;
  font-size: 13px;
}

.page-feedback {
  margin: 0;
}

:deep(.publish-table .ant-table-thead > tr > th) {
  background: #f8fafc;
  color: #4b5563;
  font-size: 12px;
  font-weight: 600;
  border-bottom: 1px solid #e6edf5;
}

:deep(.publish-table .ant-table-tbody > tr > td) {
  border-bottom: 1px solid #eef2f7;
  vertical-align: top;
  background: #ffffff;
}

:deep(.publish-table .publish-row--pending > td) {
  background: #fffbeb;
}

:deep(.publish-table .ant-table-tbody > tr:hover > td) {
  background: #f8fafc;
}

@media (max-width: 980px) {
  .table-toolbar,
  .table-footer {
    flex-direction: column;
    align-items: stretch;
  }

  .toolbar-search {
    justify-content: stretch;
  }

  .search-input {
    width: 100%;
  }
}
</style>
