<script setup lang="ts">
import { CopyOutlined, LinkOutlined } from "@ant-design/icons-vue";
import { useQuery, useQueryClient } from "@tanstack/vue-query";
import { message } from "ant-design-vue";
import type { TableColumnsType } from "ant-design-vue";
import type { ArticleVersion, PublishRecord } from "@geo/shared-types";
import { computed, ref, watch } from "vue";
import { useI18n } from "vue-i18n";

import ArticlePublishStatus from "@/components/ArticlePublishStatus.vue";
import MarkdownPreview from "@/components/MarkdownPreview.vue";
import {
  articlesApi,
  isGenerationStreamEnabled,
  subscribeArticleGeneration,
  type ArticleGenerationStreamEvent,
} from "@/lib/api";
import {
  formatDateTime,
  getGenerateStatusMeta,
  getPublishStatusMeta,
  getSourceTypeLabel,
} from "@/lib/display";

const props = defineProps<{
  open: boolean;
  articleId: number | null;
}>();

const emit = defineEmits<{
  close: [];
}>();

const queryClient = useQueryClient();
const { t } = useI18n();
const activeTab = ref("content");
const enabled = computed(() => props.open && Boolean(props.articleId));
const streamTitle = ref("");
const streamMarkdown = ref("");
const streamStatus = ref<string | null>(null);
const streamFeatureEnabled = isGenerationStreamEnabled();

const detailQuery = useQuery({
  queryKey: computed(() => ["articles", "detail", props.articleId]),
  enabled,
  queryFn: () => articlesApi.detail(props.articleId as number),
});

const versionsQuery = useQuery({
  queryKey: computed(() => ["articles", "versions", props.articleId]),
  enabled,
  queryFn: () => articlesApi.versions(props.articleId as number),
});

const publishRecordsQuery = useQuery({
  queryKey: computed(() => ["articles", "publish-records", props.articleId]),
  enabled,
  queryFn: () => articlesApi.publishRecords(props.articleId as number),
});

const versionColumns = computed<TableColumnsType<ArticleVersion>>(() => [
  {
    title: t("common.versions"),
    dataIndex: "version_no",
    key: "version_no",
    width: 88,
  },
  {
    title: t("common.title"),
    dataIndex: "title",
    key: "title",
  },
  {
    title: t("common.source"),
    dataIndex: "source_label",
    key: "source_label",
    width: 120,
  },
  {
    title: t("common.wordCount"),
    dataIndex: "word_count",
    key: "word_count",
    width: 92,
  },
  {
    title: t("common.createdAt"),
    dataIndex: "created_at",
    key: "created_at",
    width: 168,
  },
]);

const publishRecordColumns = computed<TableColumnsType<PublishRecord>>(() => [
  {
    title: t("media.records.platform"),
    dataIndex: "platform_name",
    key: "platform_name",
    width: 140,
  },
  {
    title: t("media.records.status"),
    dataIndex: "status",
    key: "status",
    width: 130,
  },
  {
    title: t("media.records.publishedAt"),
    dataIndex: "published_at",
    key: "published_at",
    width: 168,
  },
  {
    title: t("media.records.link"),
    dataIndex: "external_article_url",
    key: "external_article_url",
  },
]);

const detail = computed(() => detailQuery.data.value);
const displayTitle = computed(() => streamTitle.value || detail.value?.title || t("article.untitled"));
const displayMarkdown = computed(() => streamMarkdown.value || detail.value?.markdown_content || "");
const generateMeta = computed(() =>
  getGenerateStatusMeta(streamStatus.value || detail.value?.generate_status),
);
const sourceArticleTitle = computed(() => detail.value?.source_title?.trim() || "");
const sourceArticleURL = computed(() => detail.value?.source_url?.trim() || "");

function handleClose(): void {
  activeTab.value = "content";
  emit("close");
}

watch(
  [() => props.open, () => props.articleId],
  ([open, articleId], _prev, onCleanup) => {
    resetStreamState();

    if (!open || !articleId || !streamFeatureEnabled) {
      return;
    }

    const controller = new AbortController();
    void subscribeArticleGeneration(
      articleId,
      {
        onEvent: handleStreamEvent,
        onClose: () => {
          if (!controller.signal.aborted) {
            void refreshArticleData();
          }
        },
      },
      controller.signal,
    ).catch(() => {
      if (controller.signal.aborted) {
        return;
      }
      void refreshArticleData();
    });

    onCleanup(() => {
      controller.abort();
    });
  },
  { immediate: true },
);

watch(
  [() => props.open, () => props.articleId, () => detail.value?.generate_status],
  ([open, articleId, generateStatus], _prev, onCleanup) => {
    if (!open || !articleId || streamFeatureEnabled || generateStatus !== "generating") {
      return;
    }

    const timer = window.setInterval(() => {
      void refreshArticleData();
    }, 2000);

    onCleanup(() => {
      window.clearInterval(timer);
    });
  },
  { immediate: true },
);

function handleStreamEvent(event: ArticleGenerationStreamEvent): void {
  if (event.type === "ping") {
    return;
  }

  if (event.title) {
    streamTitle.value = event.title;
  }
  if (event.status) {
    streamStatus.value = event.status;
  }

  if (event.type === "snapshot" || event.type === "completed") {
    if (event.content) {
      streamMarkdown.value = event.content;
    }
  } else if (event.type === "delta") {
    if (event.content) {
      streamMarkdown.value = event.content;
    } else if (event.delta) {
      streamMarkdown.value += event.delta;
    }
  }

  if (event.type === "completed" || event.type === "error") {
    void refreshArticleData();
  }
}

function resetStreamState(): void {
  streamTitle.value = "";
  streamMarkdown.value = "";
  streamStatus.value = null;
}

async function refreshArticleData(): Promise<void> {
  await Promise.all([
    detailQuery.refetch(),
    versionsQuery.refetch(),
    publishRecordsQuery.refetch(),
    queryClient.invalidateQueries({ queryKey: ["articles"] }),
    queryClient.invalidateQueries({ queryKey: ["workspace"] }),
  ]);
}

function resolvePublishLink(record: PublishRecord): string {
  return record.external_article_url?.trim() || "";
}

function shouldShowClientManageHint(record: PublishRecord): boolean {
  return !resolvePublishLink(record)
    && Boolean(record.external_manage_url?.trim())
    && ["weixin_gzh", "zol"].includes(record.platform_id);
}

async function copyPublishLink(record: PublishRecord): Promise<void> {
  const target = resolvePublishLink(record);
  if (!target) {
    return;
  }

  await navigator.clipboard.writeText(target);
  message.success(t("media.records.copySuccess"));
}
</script>

<template>
  <a-drawer
    :open="open"
    width="840"
    :title="t('article.drawerTitle')"
    class="article-drawer"
    @close="handleClose"
  >
    <div v-if="detailQuery.isPending.value" class="article-drawer__loading">
      <a-skeleton active :paragraph="{ rows: 10 }" />
    </div>

    <template v-else-if="detail">
      <section class="article-drawer__hero">
        <div>
          <p class="eyebrow">{{ t("article.preview") }}</p>
          <h2>{{ displayTitle }}</h2>
          <p>
            {{ getSourceTypeLabel(detail.source_type, detail.generation_mode) }} ·
            {{ formatDateTime(detail.created_at) }}
          </p>
        </div>

        <div class="article-drawer__tags">
          <a-tag :color="generateMeta.color">{{ generateMeta.label }}</a-tag>
          <ArticlePublishStatus
            :status="detail.publish_status"
            :article-id="detail.id"
            :show-platforms="false"
          />
        </div>
      </section>

      <section class="article-drawer__meta">
        <a-descriptions :column="2" size="small" bordered>
          <a-descriptions-item :label="t('article.meta.templateType')">
            {{ detail.template_name || "--" }}
          </a-descriptions-item>
          <a-descriptions-item :label="t('article.meta.currentVersion')">
            {{ detail.version_no ?? "--" }}
          </a-descriptions-item>
          <a-descriptions-item :label="t('article.meta.source')">
            {{ detail.source_label || "--" }}
          </a-descriptions-item>
          <a-descriptions-item :label="t('article.meta.wordCount')">
            {{ detail.word_count || "--" }}
          </a-descriptions-item>
          <a-descriptions-item
            v-if="sourceArticleURL"
            :label="t('article.meta.sourceArticle')"
            :span="2"
          >
            <div class="article-drawer__source-wrapper">
              <a
                class="article-drawer__source-link"
                :href="sourceArticleURL"
                target="_blank"
                rel="noopener noreferrer"
                :title="sourceArticleURL"
              >
                <LinkOutlined class="article-drawer__source-icon" />
                <span class="article-drawer__source-copy">
                  <span v-if="sourceArticleTitle" class="article-drawer__source-title">
                    {{ sourceArticleTitle }}
                  </span>
                  <span class="article-drawer__source-url">{{ sourceArticleURL }}</span>
                </span>
              </a>
            </div>
          </a-descriptions-item>
        </a-descriptions>
      </section>

      <a-tabs v-model:activeKey="activeTab" class="article-drawer__tabs">
        <a-tab-pane key="content" :tab="t('article.preview')">
          <div v-if="displayMarkdown" class="article-drawer__content">
            <MarkdownPreview :content="displayMarkdown" />
          </div>
          <div
            v-else-if="detail.html_content"
            class="article-drawer__content"
            v-html="detail.html_content"
          />
          <a-empty v-else :description="t('article.noContent')" />
        </a-tab-pane>

        <a-tab-pane key="versions" :tab="t('article.versionHistory')">
          <a-table
            :columns="versionColumns"
            :data-source="versionsQuery.data.value || []"
            :loading="versionsQuery.isPending.value"
            :pagination="false"
            row-key="id"
            size="small"
          >
            <template #bodyCell="{ column, record }">
              <template v-if="column.key === 'title'">
                <div class="article-drawer__version-title">
                  <strong>{{ record.title || t("article.versionUntitled") }}</strong>
                  <span>{{ formatDateTime(record.created_at) }}</span>
                </div>
              </template>
              <template v-else-if="column.key === 'source_label'">
                {{ record.source_label || "--" }}
              </template>
              <template v-else-if="column.key === 'created_at'">
                {{ formatDateTime(record.created_at) }}
              </template>
            </template>
          </a-table>
        </a-tab-pane>

        <a-tab-pane key="published" :tab="t('media.records.tabTitle')">
          <a-table
            :columns="publishRecordColumns"
            :data-source="publishRecordsQuery.data.value || []"
            :loading="publishRecordsQuery.isPending.value"
            :pagination="false"
            row-key="id"
            size="small"
          >
            <template #emptyText>{{ t("media.records.empty") }}</template>
            <template #bodyCell="{ column, record }">
              <template v-if="column.key === 'platform_name'">
                <div class="article-drawer__record-platform">
                  <strong>{{ record.platform_name }}</strong>
                  <span>{{ record.platform_nickname }}</span>
                </div>
              </template>
              <template v-else-if="column.key === 'status'">
                <a-tag :color="getPublishStatusMeta(record.status).color">
                  {{ getPublishStatusMeta(record.status).label }}
                </a-tag>
              </template>
              <template v-else-if="column.key === 'published_at'">
                {{ formatDateTime(record.published_at) }}
              </template>
              <template v-else-if="column.key === 'external_article_url'">
                <div v-if="resolvePublishLink(record)" class="article-drawer__record-link-row">
                  <a
                    class="article-drawer__record-link"
                    :href="resolvePublishLink(record)"
                    target="_blank"
                    rel="noreferrer"
                  >
                    {{ resolvePublishLink(record) }}
                  </a>
                  <a-tooltip :title="t('media.records.copy')">
                    <a-button
                      type="text"
                      size="small"
                      class="article-drawer__record-copy"
                      @click="copyPublishLink(record)"
                    >
                      <template #icon><CopyOutlined /></template>
                    </a-button>
                  </a-tooltip>
                </div>
                <span v-else-if="shouldShowClientManageHint(record)" class="article-drawer__record-hint">
                  {{ t("media.records.clientManageHint") }}
                </span>
                <span v-else class="article-drawer__record-empty">--</span>
              </template>
            </template>
          </a-table>
        </a-tab-pane>
      </a-tabs>
    </template>

    <a-empty v-else :description="t('common.noData')" />
  </a-drawer>
</template>

<style scoped>
.article-drawer :deep(.ant-drawer-body) {
  padding: 0 24px 24px;
}

.article-drawer__loading {
  padding-top: 16px;
}

.article-drawer__hero {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  padding-top: 12px;
}

.article-drawer__hero h2 {
  margin: 0 0 8px;
  font-size: 24px;
  line-height: 1.2;
}

.article-drawer__hero p:last-child {
  margin: 0;
  color: var(--muted);
}

.article-drawer__tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
}

.article-drawer__meta {
  margin-top: 24px;
}

.article-drawer__source-wrapper {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  width: 100%;
}

.article-drawer__source-link {
  display: flex;
  width: 100%;
  align-items: center;
  gap: 6px;
  color: #1677ff;
}

.article-drawer__source-icon {
  flex-shrink: 0;
}

.article-drawer__source-copy {
  display: flex;
  min-width: 0;
  flex-direction: column;
  gap: 4px;
}

.article-drawer__source-title,
.article-drawer__source-url {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.article-drawer__source-title {
  color: #1f2937;
  font-weight: 500;
}

.article-drawer__source-url {
  color: #1677ff;
}

.article-drawer__alert {
  margin-top: 24px;
}

.article-drawer__tabs {
  margin-top: 24px;
}

.article-drawer__content {
  padding: 20px;
  background: #f8fafc;
  border: 1px solid #e5ecf5;
  border-radius: 18px;
}

.article-drawer__content :deep(h1),
.article-drawer__content :deep(h2),
.article-drawer__content :deep(h3) {
  margin-top: 0;
}

.article-drawer__version-title {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.article-drawer__version-title span {
  color: var(--muted);
  font-size: 12px;
}

.article-drawer__record-platform {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.article-drawer__record-platform span,
.article-drawer__record-empty {
  color: var(--muted);
  font-size: 12px;
}

.article-drawer__record-hint {
  color: var(--muted);
  font-size: 12px;
}

.article-drawer__record-link {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  color: #1677ff;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.article-drawer__record-link-row {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.article-drawer__record-copy.ant-btn {
  flex: none;
  color: #8c8c8c;
}

.article-drawer__record-copy.ant-btn:hover:not(:disabled) {
  color: #1677ff;
  background: #e6f4ff;
}

@media (max-width: 960px) {
  .article-drawer__hero {
    flex-direction: column;
  }

  .article-drawer__tags {
    justify-content: flex-start;
  }
}
</style>
