<script setup lang="ts">
import {
  ClockCircleOutlined,
  ExportOutlined,
  ReloadOutlined,
  SearchOutlined,
  SendOutlined,
} 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 { useDesktopRuntime } from "../composables/useDesktopRuntime";
import { formatDateTime, formatRelativeTime, titleCaseToken } from "../lib/formatters";
import { desktopPublishMediaCatalog } from "../lib/media-catalog";
import { normalizePublisherErrorMessage } from "../../shared/publisher-errors";

const PAGE_SIZE = 10;

const publishPlatformIndex: Record<string, (typeof desktopPublishMediaCatalog)[number]> = Object.fromEntries(
  desktopPublishMediaCatalog.map((item) => [item.id, item]),
);

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 platformMeta(platform: string) {
  const key = (platform ?? "").toLowerCase();
  return publishPlatformIndex[key] ?? null;
}

function translatePlatform(platform: string) {
  return platformMeta(platform)?.label || titleCaseToken(platform);
}

function platformLogo(platform: string): string | null {
  return platformMeta(platform)?.logoUrl ?? null;
}

function platformAccent(platform: string): string {
  return platformMeta(platform)?.accent ?? "#64748b";
}

function platformShortName(platform: string): string {
  return platformMeta(platform)?.shortName ?? titleCaseToken(platform).slice(0, 1);
}

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 normalizePublisherErrorMessage(normalized) ?? 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;
  }
}

async function openExternalUrl(url: string | null) {
  if (!url) {
    return;
  }
  actionError.value = null;

  try {
    await window.desktopBridge.app.openExternalUrl(url);
  } catch (err) {
    actionError.value = err instanceof Error ? err.message : "打开外链失败";
  }
}

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-container">
    <section class="hero-card">
      <div class="hero-header">
        <div class="hero-title">
          <p class="eyebrow">PUBLISH EXECUTION DESK</p>
          <h2>发布管理</h2>
          <p class="summary">
            统一用一个 table 查看当前设备负责的待发布队列与已发送历史。页面固定每页 10 条，待发布任务会始终排在最前面，只有当前页的 PublishTasks 展示完后，才会继续补已发送历史记录。
          </p>
        </div>
        <div class="hero-actions">
          <a-button type="primary" ghost class="modern-btn" :loading="loading" @click="refreshTasks()">
            <template #icon><ReloadOutlined /></template>
            刷新状态
          </a-button>
        </div>
      </div>
      <div class="stats-strip">
        <div class="stat-card">
          <div class="stat-label">历史总数</div>
          <div class="stat-value">{{ totalCount }}</div>
        </div>
        <div class="stat-card">
          <div class="stat-label">等待发布</div>
          <div class="stat-value">{{ pendingCount }}</div>
        </div>
        <div class="stat-card">
          <div class="stat-label">已发送</div>
          <div class="stat-value">{{ historyTotal }}</div>
        </div>
      </div>
    </section>

    <!-- Feedback Banners -->
    <div v-if="actionMessage" class="feedback-banner success"><div class="success-text">{{ actionMessage }}</div></div>
    <div v-if="error" class="feedback-banner error"><div class="error-text">{{ error }}</div></div>
    <div v-if="actionError" class="feedback-banner error"><div class="error-text">{{ actionError }}</div></div>

    <!-- Table Details Section -->
    <section class="content-section">
      <div class="hero-card" style="padding-bottom: 0;">
        <div class="hero-header" style="padding: 24px 32px 16px; border-bottom: 1px solid #eef2f6; align-items: center;">
          <div style="flex: 1;">
            <h3 class="section-title">待发布与已发送记录</h3>
            <p class="section-desc" style="margin-top: 4px;">支持按文章标题搜索，分页与总数以服务端返回的 PublishTasks 结果为准。</p>
          </div>
          
          <!-- Filters ToolBar -->
          <div style="display: flex; gap: 12px; align-items: center;">
            <a-input
              v-model:value="searchTitle"
              allow-clear
              placeholder="搜索文章标题"
              style="width: 280px;"
              @pressEnter="applySearch"
            >
              <template #prefix>
                <SearchOutlined style="color: #94a3b8" />
              </template>
            </a-input>
            <a-button type="primary" class="modern-btn" style="border-radius: 8px; font-weight: 500;" @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>
            <div style="padding: 60px; color: #94a3b8;"><a-empty description="暂无匹配的发布任务" /></div>
          </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 platform-line">
                    <span class="platform-logo-mini">
                      <img
                        v-if="platformLogo(record.platform)"
                        :src="platformLogo(record.platform) || ''"
                        :alt="translatePlatform(record.platform)"
                      />
                      <span
                        v-else
                        class="platform-logo-fallback"
                        :style="{ color: platformAccent(record.platform) }"
                      >
                        {{ platformShortName(record.platform) }}
                      </span>
                    </span>
                    <span>{{ translatePlatform(record.platform) }}</span>
                  </span>
                </div>
              </div>
            </template>

            <template v-else-if="column.key === 'status'">
              <div class="status-cell">
                <StatusBadge :tone="statusTone(record.status)" :label="statusLabel(record.status)" />
              </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 mono-detail">
                    尝试 {{ record.attempts }} 次
                    <template v-if="record.leaseExpiresAt"> · 租约 {{ formatRelativeTime(record.leaseExpiresAt) }} 到期</template>
                    <template v-else-if="record.externalArticleUrl">
                      ·
                      <a-tooltip :title="record.externalArticleUrl" overlay-class-name="external-url-tooltip">
                        <a
                          class="external-task-link"
                          :href="record.externalArticleUrl"
                          target="_blank"
                          rel="noopener noreferrer"
                          @click.prevent.stop="openExternalUrl(record.externalArticleUrl)"
                        >
                          <ExportOutlined />
                          打开外链
                        </a>
                      </a-tooltip>
                    </template>
                    <template v-else-if="record.externalManageUrl">
                      ·
                      <a-tooltip :title="record.externalManageUrl" overlay-class-name="external-url-tooltip">
                        <a
                          class="external-task-link"
                          :href="record.externalManageUrl"
                          target="_blank"
                          rel="noopener noreferrer"
                          @click.prevent.stop="openExternalUrl(record.externalManageUrl)"
                        >
                          <ExportOutlined />
                          打开管理页
                        </a>
                      </a-tooltip>
                    </template>
                  </span>
                </div>
              </div>
            </template>

            <template v-else-if="column.key === 'time'">
              <div class="cell-primary-secondary">
                <div class="info-stack">
                  <span class="title mono-text">{{ 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-popconfirm
                  v-if="!isPendingStatus(record.status)"
                  title="确定要重试本次发布吗？"
                  placement="topRight"
                  ok-text="确定"
                  cancel-text="取消"
                  @confirm="retryTask(record.id)"
                >
                  <a-tooltip title="再次发送" placement="top">
                    <a-button
                      type="text"
                      class="icon-action-btn"
                      :loading="actionPendingTaskId === record.id"
                    >
                      <template #icon><SendOutlined /></template>
                    </a-button>
                  </a-tooltip>
                </a-popconfirm>
                <a-tooltip v-else title="排队中...">
                  <div class="action-placeholder-icon">
                    <ClockCircleOutlined />
                  </div>
                </a-tooltip>
              </div>
            </template>
          </template>
        </a-table>

        <div class="table-footer">
          <a-pagination
            :current="currentPage"
            :page-size="PAGE_SIZE"
            :total="totalCount"
            :show-size-changer="false"
            @change="handlePageChange"
          />
        </div>
      </div>
    </section>
  </section>
</template>

<style scoped>
.page-container {
  display: flex;
  flex-direction: column;
  gap: 32px;
  padding-bottom: 40px;
  max-width: 1400px;
}

/* Hero Section */
.hero-card {
  background: #ffffff;
  border-radius: 20px;
  border: 1px solid #eef2f6;
  box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px -2px rgba(0, 0, 0, 0.01);
  overflow: hidden;
}

.hero-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 36px 40px;
}

.eyebrow {
  margin: 0 0 12px;
  color: #64748b;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.hero-title h2 {
  margin: 0;
  font-size: 32px;
  font-weight: 800;
  color: #0f172a;
  letter-spacing: -0.02em;
}

.summary {
  margin: 16px 0 0;
  max-width: 800px;
  color: #475569;
  line-height: 1.6;
  font-size: 15px;
}

.modern-btn {
  border-radius: 8px;
  height: 40px;
  padding: 0 20px;
  font-weight: 600;
}

/* Stats Strip */
.stats-strip {
  display: flex;
  padding: 24px 40px;
  background: #f8fafc;
  border-top: 1px solid #eef2f6;
  gap: 40px;
}

.stat-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1;
}

.stat-label {
  font-size: 14px;
  font-weight: 500;
  color: #64748b;
}

.stat-value {
  font-size: 40px;
  font-weight: 700;
  color: #0f172a;
  line-height: 1;
  font-feature-settings: "tnum";
}

/* Content Section */
.content-section {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.section-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: #0f172a;
}

.section-desc {
  margin: 8px 0 0;
  color: #64748b;
  font-size: 15px;
  line-height: 1.5;
}

/* Modern Enterprise Table Styles */
:deep(.modern-table) {
  padding: 0 32px;
}

:deep(.modern-table .ant-table-thead > tr > th) {
  background-color: transparent !important;
  color: #64748b;
  font-weight: 600;
  font-size: 13px;
  border-bottom: 1px solid #e2e8f0;
  padding: 16px 12px;
}

:deep(.modern-table .ant-table-tbody > tr > td) {
  padding: 16px 12px;
  border-bottom: 1px solid #f1f5f9;
  color: #0f172a;
  vertical-align: top;
  transition: background-color 0.2s ease;
}

:deep(.modern-table .ant-table-tbody > tr:last-child > td) {
  border-bottom: none;
}

:deep(.modern-table .ant-table-tbody > tr:hover > td) {
  background-color: #f8fafc !important;
}

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

.cell-primary-secondary {
  display: flex;
  flex-direction: column;
}
.info-stack {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.info-stack .title {
  font-weight: 600;
  color: #0f172a;
  font-size: 14px;
  line-height: 1.5;
}
.info-stack .subtitle {
  color: #64748b;
  font-size: 13px;
  line-height: 1.5;
}
.info-stack .mono-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
.info-stack .mono-detail {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 12px;
  color: #94a3b8;
}

.platform-line {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.platform-logo-mini {
  width: 18px;
  height: 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #ffffff;
  border-radius: 4px;
  box-shadow: inset 0 0 0 1px #e2e8f0;
  overflow: hidden;
  flex: 0 0 auto;
}
.platform-logo-mini img {
  width: 14px;
  height: 14px;
  object-fit: contain;
}
.platform-logo-fallback {
  font-size: 11px;
  font-weight: 700;
  line-height: 1;
}

.external-task-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  color: #2563eb;
  font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  text-decoration: none;
}

.external-task-link:hover {
  color: #1d4ed8;
  text-decoration: underline;
}

.info-stack .strong-text {
  color: #1e293b;
  font-weight: 500;
}

.error-text {
  color: #ef4444;
  font-size: 13px;
  margin-top: 4px;
  font-weight: 500;
}
.success-text {
  color: #10b981;
  font-size: 13px;
  font-weight: 500;
}

/* Action Buttons */
.action-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.icon-action-btn {
  color: #64748b;
  border-radius: 8px;
  transition: all 0.2s ease;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid transparent;
}

.icon-action-btn:hover {
  background: #f1f5f9;
  color: #0ea5e9;
  border-color: #e2e8f0;
}

.action-placeholder-icon {
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: #94a3b8;
  font-size: 16px;
}

.table-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 32px 32px;
  border-top: 1px solid #eef2f6;
  background: transparent;
}

.table-note {
  color: #94a3b8;
  font-size: 13px;
}

.feedback-banner {
  padding: 16px 20px;
  border-radius: 12px;
  background: #fff;
  border: 1px solid transparent;
}
.feedback-banner.error {
  border-color: #fca5a5;
  background: #fef2f2;
}
.feedback-banner.success {
  border-color: #6ee7b7;
  background: #ecfdf5;
}

:global(.external-url-tooltip .ant-tooltip-inner) {
  max-width: min(520px, 80vw);
  overflow-wrap: anywhere;
}

/* Responsiveness */
@media (max-width: 1024px) {
  .hero-header {
    flex-direction: column;
    gap: 24px;
  }
  .stats-strip {
    flex-wrap: wrap;
    gap: 24px;
  }
}
</style>
