<script setup lang="ts">
import {
  ReloadOutlined,
} from "@ant-design/icons-vue";
import { useMutation, useQuery } from "@tanstack/vue-query";
import { useStorage } from "@vueuse/core";
import { message } from "ant-design-vue";
import dayjs from "dayjs";
import type {
  Brand,
  DesktopAccountInfo,
  MonitoringHotQuestion,
  MonitoringTimeBucket,
} from "@geo/shared-types";
import { aiPlatformCatalog } from "@geo/shared-types";
import { computed, watch } from "vue";
import { useI18n } from "vue-i18n";
import { useRoute, useRouter } from "vue-router";

import PageHero from "@/components/PageHero.vue";
import { brandsApi, monitoringApi, tenantAccountsApi } from "@/lib/api";
import { formatDateTime } from "@/lib/display";
import { formatError } from "@/lib/errors";

const { t } = useI18n();
const route = useRoute();
const router = useRouter();

const trackingMaxHistoryDays = 30;
const trackingBusinessTimeZone = "Asia/Shanghai";
const trackingToday = formatTrackingBusinessDate(new Date());

type CitationRankingRow = {
  ai_platform_id: string;
  platform_name: string;
  cited_answer_count: number | null;
  cited_article_count: number | null;
  citation_rate: number | null;
  share: number;
  color: string;
  surface: string;
  glyph: string;
};

type AIPlatformStatusCard = {
  id: string;
  label: string;
  shortName: string;
  accent: string;
  description: string;
  account: DesktopAccountInfo | null;
  sampleStatus: string;
  lastSampledAt: string | null;
  actualSampleCount: number;
};

function formatTrackingBusinessDate(value: Date): string {
  const parts = new Intl.DateTimeFormat("en-US", {
    timeZone: trackingBusinessTimeZone,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).formatToParts(value);

  const year = parts.find((part) => part.type === "year")?.value;
  const month = parts.find((part) => part.type === "month")?.value;
  const day = parts.find((part) => part.type === "day")?.value;
  if (!year || !month || !day) {
    throw new Error("tracking_business_date_format_failed");
  }

  return `${year}-${month}-${day}`;
}

const platformAppearanceMap: Record<string, { color: string; surface: string; glyph: string }> = {
  doubao: { color: "#ff9348", surface: "#fff2e8", glyph: "豆" },
  deepseek: { color: "#5d72ff", surface: "#eef1ff", glyph: "D" },
  qwen: { color: "#276ef1", surface: "#edf4ff", glyph: "通" },
  tongyi: { color: "#6d5efc", surface: "#f1efff", glyph: "通" },
  tongyiqianwen: { color: "#6d5efc", surface: "#f1efff", glyph: "通" },
  wenxin: { color: "#20c6d7", surface: "#ebfcff", glyph: "文" },
  wenxinyiyan: { color: "#20c6d7", surface: "#ebfcff", glyph: "文" },
  yuanbao: { color: "#19bf6b", surface: "#eafbf1", glyph: "元" },
  kimi: { color: "#111827", surface: "#f3f4f6", glyph: "K" },
};

const trackingPlatformOptions = [
  { label: "全部", value: "all" },
  ...aiPlatformCatalog.map((platform) => ({
    label: platform.label,
    value: platform.id,
  })),
] as const;

const trackingPlatformIds = new Set<string>(
  aiPlatformCatalog.map((item) => item.id),
);

const selectedBrandId = useStorage<number | null>("tracking_selected_brand", null);
const selectedKeywordId = useStorage<number | null>("tracking_selected_keyword", null);
const selectedPlatformId = useStorage<string>("tracking_selected_ai_platform_id", "all");
const selectedBusinessDate = useStorage<string>("tracking_selected_business_date", trackingToday);

selectedPlatformId.value = normalizeTrackingPlatformId(selectedPlatformId.value);
selectedBusinessDate.value = normalizeTrackingBusinessDate(selectedBusinessDate.value) ?? trackingToday;

const brandsQuery = useQuery({
  queryKey: ["tracking", "brands"],
  queryFn: () => brandsApi.list(),
});

watch(
  () => brandsQuery.data.value,
  (brands) => {
    if (!brands?.length) {
      return;
    }

    const requestedBrandId = parseNumericQuery(route.query.brand_id);
    if (requestedBrandId && brands.some((item) => item.id === requestedBrandId)) {
      selectedBrandId.value = requestedBrandId;
      return;
    }

    if (selectedBrandId.value && brands.some((item) => item.id === selectedBrandId.value)) {
      return;
    }

    selectedBrandId.value = brands[0].id;
  },
  { immediate: true },
);

const keywordsQuery = useQuery({
  queryKey: computed(() => ["tracking", "keywords", selectedBrandId.value]),
  enabled: computed(() => Boolean(selectedBrandId.value)),
  queryFn: () => brandsApi.listKeywords(selectedBrandId.value as number),
});

watch(
  () => keywordsQuery.data.value,
  (keywords) => {
    if (!keywords?.length) {
      return;
    }

    const requestedKeywordId = parseNumericQuery(route.query.keyword_id);
    if (requestedKeywordId && keywords.some((item) => item.id === requestedKeywordId)) {
      selectedKeywordId.value = requestedKeywordId;
      return;
    }

    if (selectedKeywordId.value && keywords.some((item) => item.id === selectedKeywordId.value)) {
      return;
    }

    selectedKeywordId.value = keywords[0].id;
  },
  { immediate: true },
);

watch(selectedBrandId, (newId, oldId) => {
  if (oldId && newId !== oldId) {
    selectedKeywordId.value = null;
  }
});

watch(
  () => route.query.business_date,
  (value) => {
    const requestedBusinessDate = normalizeTrackingBusinessDate(value);
    if (requestedBusinessDate) {
      selectedBusinessDate.value = requestedBusinessDate;
    }
  },
  { immediate: true },
);

watch(
  () => route.query.ai_platform_id,
  (value) => {
    selectedPlatformId.value = normalizeTrackingPlatformId(value);
  },
  { immediate: true },
);

watch([selectedBrandId, selectedKeywordId, selectedPlatformId, selectedBusinessDate], ([brandId, keywordId, platformId, businessDate]) => {
  const nextBrandId = brandId ? String(brandId) : undefined;
  const nextKeywordId = keywordId ? String(keywordId) : undefined;
  const nextPlatformId = platformId !== "all" ? platformId : undefined;
  const nextBusinessDate = normalizeTrackingBusinessDate(businessDate) ?? trackingToday;
  const currentBrandId = normalizeQueryValue(route.query.brand_id) || undefined;
  const currentKeywordId = normalizeQueryValue(route.query.keyword_id) || undefined;
  const currentPlatformId = normalizeTrackingPlatformId(route.query.ai_platform_id);
  const currentBusinessDate = normalizeTrackingBusinessDate(route.query.business_date) ?? trackingToday;

  if (
    currentBrandId === nextBrandId &&
    currentKeywordId === nextKeywordId &&
    currentPlatformId === (nextPlatformId ?? "all") &&
    currentBusinessDate === nextBusinessDate
  ) {
    return;
  }

  void router.replace({
    name: "tracking",
    query: {
      ...(nextBrandId ? { brand_id: nextBrandId } : {}),
      ...(nextKeywordId ? { keyword_id: nextKeywordId } : {}),
      ...(nextPlatformId ? { ai_platform_id: nextPlatformId } : {}),
      business_date: nextBusinessDate,
    },
  });
});

const trackingWindow = computed(() => {
  const end = dayjs(selectedBusinessDate.value);
  const earliestAllowed = dayjs(trackingToday).subtract(trackingMaxHistoryDays - 1, "day").startOf("day");
  const candidateFrom = end.subtract(trackingMaxHistoryDays - 1, "day");
  const from = candidateFrom.isBefore(earliestAllowed, "day") ? earliestAllowed : candidateFrom;
  return {
    from: from.format("YYYY-MM-DD"),
    to: end.format("YYYY-MM-DD"),
  };
});

const dashboardQuery = useQuery({
  queryKey: computed(() => [
    "tracking",
    "dashboard",
    selectedBrandId.value,
    selectedKeywordId.value,
    selectedPlatformId.value,
    selectedBusinessDate.value,
  ]),
  enabled: computed(() => Boolean(selectedBrandId.value)),
  queryFn: () =>
    monitoringApi.dashboardComposite({
      brand_id: selectedBrandId.value ?? undefined,
      keyword_id: selectedKeywordId.value,
      days: trackingMaxHistoryDays,
      ai_platform_id: selectedPlatformId.value !== "all" ? selectedPlatformId.value : undefined,
      business_date: selectedBusinessDate.value,
    }),
});

const aiAccountsQuery = useQuery({
  queryKey: ["tracking", "ai-platform-accounts"],
  queryFn: () => tenantAccountsApi.list(),
});

const collectNowMutation = useMutation({
  mutationFn: async () => {
    if (!selectedBrandId.value) {
      throw new Error("tracking_brand_required");
    }
    if (!selectedKeywordId.value) {
      throw new Error("tracking_keyword_required");
    }

    return monitoringApi.collectNow(selectedBrandId.value, {
      keyword_id: selectedKeywordId.value,
    });
  },
  onSuccess: (collectNowResult) => {
    message.success(collectNowResult.message);
    void dashboardQuery.refetch();
  },
  onError: (error) => {
    if (error instanceof Error && error.message === "tracking_brand_required") {
      message.warning(t("tracking.collectBrandRequired"));
      return;
    }
    if (error instanceof Error && error.message === "tracking_keyword_required") {
      message.warning(t("tracking.collectKeywordRequired"));
      return;
    }
    message.error(formatError(error));
  },
});

const selectedBrand = computed<Brand | null>(() => {
  return brandsQuery.data.value?.find((item) => item.id === selectedBrandId.value) ?? null;
});

const currentUserClientOnline = computed(() => {
  return dashboardQuery.data.value?.runtime.current_user_client_online === true;
});

const collectNowDisabledReason = computed(() => {
  if (!selectedBrandId.value) {
    return t("tracking.collectBrandRequired");
  }
  if (!selectedKeywordId.value) {
    return t("tracking.collectKeywordRequired");
  }
  if (dashboardQuery.isLoading.value && !dashboardQuery.data.value) {
    return t("tracking.collectClientChecking");
  }
  if (!currentUserClientOnline.value) {
    return t("tracking.collectClientOnlineRequired");
  }
  return null;
});

const selectedDateHasData = computed(() => {
  return (dashboardQuery.data.value?.overview.actual_sample_count ?? 0) > 0;
});

const hotQuestions = computed(() => {
  return dashboardQuery.data.value?.hot_questions ?? [];
});

const aiPlatformStatusCards = computed<AIPlatformStatusCard[]>(() => {
  const accountGroups = new Map<string, DesktopAccountInfo[]>();
  for (const account of aiAccountsQuery.data.value ?? []) {
    if (!trackingPlatformIds.has(account.platform) || account.deleted_at) {
      continue;
    }
    const list = accountGroups.get(account.platform) ?? [];
    list.push(account);
    accountGroups.set(account.platform, list);
  }

  const breakdownMap = new Map(
    (dashboardQuery.data.value?.platform_breakdown ?? []).map((item) => [item.ai_platform_id, item]),
  );

  return aiPlatformCatalog.map((platform) => {
    const matchedAccounts = [...(accountGroups.get(platform.id) ?? [])].sort((left, right) => {
      const leftAt = Date.parse(left.verified_at ?? "") || 0;
      const rightAt = Date.parse(right.verified_at ?? "") || 0;
      return rightAt - leftAt;
    });
    const breakdown = breakdownMap.get(platform.id);

    return {
      id: platform.id,
      label: platform.label,
      shortName: platform.shortName,
      accent: platform.accent,
      description: platform.description,
      account: matchedAccounts[0] ?? null,
      sampleStatus: breakdown?.platform_sample_status ?? "pending",
      lastSampledAt: breakdown?.last_sampled_at ?? null,
      actualSampleCount: breakdown?.actual_sample_count ?? 0,
    };
  });
});

const selectedMetricCards = computed(() => {
  const overview = dashboardQuery.data.value?.overview;
  const buckets = dashboardQuery.data.value?.brand_time_buckets ?? [];
  const points = selectedDateHasData.value ? buckets : [];
  return [
    {
      key: "mention",
      title: t("tracking.metrics.mentionRate"),
      value: overview?.mention_rate ?? null,
      previous: overview?.prev_snapshot_mention_rate ?? null,
      points: points.map((item) => item.mention_rate),
    },
    {
      key: "top1",
      title: t("tracking.metrics.top1MentionRate"),
      value: overview?.top1_mention_rate ?? null,
      previous: overview?.prev_snapshot_top1_mention_rate ?? null,
      points: points.map((item) => item.top1_mention_rate),
    },
    {
      key: "first",
      title: t("tracking.metrics.firstRecommendRate"),
      value: overview?.first_recommend_rate ?? null,
      previous: overview?.prev_snapshot_first_recommend_rate ?? null,
      points: points.map((item) => item.first_recommend_rate),
    },
    {
      key: "positive",
      title: t("tracking.metrics.positiveMentionRate"),
      value: overview?.positive_mention_rate ?? null,
      previous: overview?.prev_snapshot_positive_mention_rate ?? null,
      points: points.map((item) => item.positive_mention_rate),
    },
  ];
});

const coverageSummary = computed(() => {
  const overview = dashboardQuery.data.value?.overview;
  if (!overview) {
    return "";
  }
  if (!selectedDateHasData.value) {
    return `${selectedBusinessDate.value} 暂无抓取数据`;
  }
  return t("tracking.coverageSummary", {
    desired: overview.desired_sample_count,
    actual: overview.actual_sample_count,
    coverage: formatPercent(overview.coverage_rate),
  });
});

const citationRankingRows = computed<CitationRankingRow[]>(() => {
  if (!selectedDateHasData.value) {
    return [];
  }

  const breakdown = dashboardQuery.data.value?.platform_breakdown ?? [];
  const ranking = dashboardQuery.data.value?.citation_ranking ?? [];
  const order = new Map<string, number>();
  const rows = new Map<string, CitationRankingRow>();

  breakdown.forEach((item, index) => {
    order.set(item.ai_platform_id, index);
    rows.set(item.ai_platform_id, {
      ai_platform_id: item.ai_platform_id,
      platform_name: item.platform_name,
      cited_answer_count: null,
      cited_article_count: null,
      citation_rate: null,
      share: 0,
      ...platformAppearance(item.ai_platform_id, item.platform_name),
    });
  });

  ranking.forEach((item, index) => {
    if (!order.has(item.ai_platform_id)) {
      order.set(item.ai_platform_id, breakdown.length + index);
    }

    const existing = rows.get(item.ai_platform_id);
    rows.set(item.ai_platform_id, {
      ai_platform_id: item.ai_platform_id,
      platform_name: item.platform_name || existing?.platform_name || item.ai_platform_id,
      cited_answer_count: item.cited_answer_count,
      cited_article_count: item.cited_article_count,
      citation_rate: item.citation_rate,
      share: 0,
      ...platformAppearance(item.ai_platform_id, item.platform_name || existing?.platform_name || item.ai_platform_id),
    });
  });

  const list = Array.from(rows.values());
  const totalAppearances = list.reduce((sum, item) => sum + Math.max(item.cited_answer_count ?? 0, 0), 0);
  const enriched = list.map((item) => ({
    ...item,
    share: totalAppearances > 0 ? Math.max(item.cited_answer_count ?? 0, 0) / totalAppearances : 0,
  }));
  const hasCitationData = enriched.some((item) => (item.cited_answer_count ?? 0) > 0);

  return enriched.sort((left, right) => {
    if (hasCitationData) {
      const countDiff = (right.cited_answer_count ?? 0) - (left.cited_answer_count ?? 0);
      if (countDiff !== 0) {
        return countDiff;
      }
    }
    return (order.get(left.ai_platform_id) ?? 0) - (order.get(right.ai_platform_id) ?? 0);
  });
});

const visibleCitedArticles = computed(() => {
  if (!selectedDateHasData.value) {
    return [];
  }
  return dashboardQuery.data.value?.cited_articles ?? [];
});

const citationChartStyle = computed(() => {
  const segments = citationRankingRows.value.filter((item) => (item.cited_answer_count ?? 0) > 0);

  if (!segments.length) {
    return {
      background: "linear-gradient(180deg, #eef4ff 0%, #e5ecff 100%)",
    };
  }

  let offset = 0;
  const gradientStops = segments.map((item) => {
    const next = offset + item.share * 360;
    const stop = `${item.color} ${offset.toFixed(2)}deg ${next.toFixed(2)}deg`;
    offset = next;
    return stop;
  });

  return {
    background: `conic-gradient(${gradientStops.join(", ")})`,
  };
});

const heroDescription = computed(() => {
  const overview = dashboardQuery.data.value?.overview;
  if (!overview?.last_sampled_at) {
    return `${selectedBusinessDate.value} 暂无抓取数据`;
  }
  return `最近一次采集数据：${formatDateTime(overview.last_sampled_at)}`;
});

function openQuestion(question: MonitoringHotQuestion): void {
  if (!selectedBrandId.value) {
    return;
  }

  void router.push({
    name: "tracking-question-detail",
    params: {
      brandId: String(selectedBrandId.value),
      questionId: String(question.question_id),
    },
    query: {
      question_hash: question.question_hash,
      question_text: question.question_text,
      business_date: selectedBusinessDate.value,
      date_from: selectedBusinessDate.value,
      date_to: selectedBusinessDate.value,
      ...(selectedPlatformId.value !== "all" ? { ai_platform_id: selectedPlatformId.value } : {}),
      ...(selectedKeywordId.value ? { keyword_id: String(selectedKeywordId.value) } : {}),
    },
  });
}

function formatPercent(value: number | null | undefined): string {
  if (value === null || value === undefined) {
    return "--";
  }
  return `${(value * 100).toFixed(value < 0.1 ? 1 : 0)}%`;
}

function accountAuthLabel(account: DesktopAccountInfo | null): string {
  if (!account) {
    return "未绑定";
  }

  switch (account.health) {
    case "live":
      return "授权正常";
    case "captcha":
      return "待处理";
    case "expired":
      return "授权过期";
    default:
      return "风险观察";
  }
}

function accountAuthColor(account: DesktopAccountInfo | null): string {
  if (!account) {
    return "default";
  }

  switch (account.health) {
    case "live":
      return "green";
    case "captcha":
      return "gold";
    case "expired":
      return "red";
    default:
      return "orange";
  }
}

function desktopStatusLabel(account: DesktopAccountInfo | null): string {
  if (!account?.client_id) {
    return "未绑定桌面端";
  }

  return account.client_online ? "客户端在线" : "客户端离线";
}

function formatCount(value: number | null | undefined): string {
  if (value === null || value === undefined) {
    return "--";
  }
  return String(value);
}

function formatDelta(current: number | null | undefined, previous: number | null | undefined): string | null {
  if (current === null || current === undefined || previous === null || previous === undefined) {
    return null;
  }
  const diff = current - previous;
  if (Math.abs(diff) < 0.001) {
    return t("tracking.noChange");
  }
  const prefix = diff > 0 ? "+" : "";
  return `${prefix}${(diff * 100).toFixed(1)}%`;
}

function bucketPointClass(bucket: MonitoringTimeBucket, point: number | null): string {
  if (point === null || point === undefined) {
    return "metric-card__point metric-card__point--empty";
  }
  if (bucket.sample_status === "partial") {
    return "metric-card__point metric-card__point--partial";
  }
  return "metric-card__point metric-card__point--solid";
}

function dayDotClass(bucket: MonitoringTimeBucket): string {
  if (bucket.sample_status === "sampled") {
    return "tracking-day-dot tracking-day-dot--sampled";
  }
  if (bucket.sample_status === "partial") {
    return "tracking-day-dot tracking-day-dot--partial";
  }
  return "tracking-day-dot";
}

function formatTriggerSource(value: string): string {
  return value === "manual" ? t("tracking.manual") : t("tracking.automatic");
}

function statusTagColor(status: string): string {
  switch (status) {
    case "sampled":
      return "green";
    case "pending":
      return "gold";
    case "not_logged_in":
      return "default";
    case "unavailable":
      return "red";
    default:
      return "default";
  }
}

function statusLabel(status: string): string {
  return t(`tracking.status.${status}`);
}

function platformAppearance(aiPlatformId: string, platformName: string) {
  const normalizedId = aiPlatformId.toLowerCase().replace(/[^a-z0-9]/g, "");
  const matched = platformAppearanceMap[normalizedId];

  if (matched) {
    return matched;
  }

  const glyph = platformName.trim().slice(0, 1).toUpperCase() || "?";
  return {
    color: "#f5b13d",
    surface: "#fff7e8",
    glyph,
  };
}

function parseNumericQuery(value: unknown): number | null {
  const raw = Array.isArray(value) ? value[0] : value;
  const parsed = Number(raw);
  if (!Number.isFinite(parsed) || parsed <= 0) {
    return null;
  }
  return parsed;
}

function normalizeQueryValue(value: unknown): string {
  return String(Array.isArray(value) ? value[0] ?? "" : value ?? "").trim();
}

function normalizeTrackingPlatformId(value: unknown): string {
  const normalized = normalizeQueryValue(value).toLowerCase();
  if (!normalized) {
    return "all";
  }
  return trackingPlatformIds.has(normalized) ? normalized : "all";
}

function normalizeTrackingBusinessDate(value: unknown): string | null {
  const normalized = normalizeQueryValue(value);
  if (!normalized) {
    return null;
  }
  const parsed = dayjs(normalized);
  if (!parsed.isValid() || parsed.format("YYYY-MM-DD") !== normalized) {
    return null;
  }

  const today = dayjs().startOf("day");
  const earliestAllowed = today.subtract(trackingMaxHistoryDays - 1, "day");
  if (parsed.isAfter(today, "day") || parsed.isBefore(earliestAllowed, "day")) {
    return null;
  }
  return normalized;
}

function disableTrackingDate(current: dayjs.Dayjs): boolean {
  const today = dayjs().endOf("day");
  const earliestAllowed = dayjs().subtract(trackingMaxHistoryDays - 1, "day").startOf("day");
  return current.isAfter(today) || current.isBefore(earliestAllowed);
}
</script>

<template>
  <section class="tracking-view">
    <PageHero
      :eyebrow="t('tracking.eyebrow')"
      :title="t('route.tracking.title')"
      :description="heroDescription"
    >
      <template #actions>
        <a-space wrap align="center" class="tracking-actions-wrap">
          <div class="tracking-action-item">
            <span class="tracking-action-label">平台</span>
            <a-select
              v-model:value="selectedPlatformId"
              class="tracking-select"
              :options="trackingPlatformOptions"
            />
          </div>
          <div class="tracking-action-item">
            <span class="tracking-action-label">品牌</span>
            <a-select
              v-model:value="selectedBrandId"
              class="tracking-select"
              :placeholder="t('tracking.brandPlaceholder')"
              :options="(brandsQuery.data.value ?? []).map((item) => ({ label: item.name, value: item.id }))"
            />
          </div>
          <div class="tracking-action-item">
            <span class="tracking-action-label">关键词</span>
            <a-select
              v-model:value="selectedKeywordId"
              allow-clear
              class="tracking-select"
              :placeholder="t('tracking.keywordPlaceholder')"
              :options="(keywordsQuery.data.value ?? []).map((item) => ({ label: item.name, value: item.id }))"
            />
          </div>
          <div class="tracking-action-item">
            <span class="tracking-action-label">日期</span>
            <a-date-picker
              v-model:value="selectedBusinessDate"
              class="tracking-date-picker"
              format="YYYY-MM-DD"
              value-format="YYYY-MM-DD"
              :allow-clear="false"
              :disabled-date="disableTrackingDate"
            />
          </div>
          <a-tooltip :title="collectNowDisabledReason ?? undefined">
            <span>
              <a-button
                type="primary"
                :disabled="Boolean(collectNowDisabledReason)"
                :loading="collectNowMutation.isPending.value"
                @click="collectNowMutation.mutate()"
              >
                <template #icon><ReloadOutlined /></template>
                {{ t("tracking.collectNow") }}
              </a-button>
            </span>
          </a-tooltip>
        </a-space>
      </template>
    </PageHero>

    <a-alert
      v-if="dashboardQuery.error.value"
      type="error"
      show-icon
      :message="formatError(dashboardQuery.error.value)"
      class="tracking-view__alert"
    />

    <div class="tracking-grid tracking-grid--top">
      <a-card class="tracking-panel tracking-panel--overview" :loading="dashboardQuery.isLoading.value">
        <div class="tracking-panel__header">
          <h3 class="panel-title">{{ selectedBrand?.name ?? t("common.noData") }}</h3>
          <a-tag :color="selectedDateHasData ? (dashboardQuery.data.value?.overview.confidence_level === 'high' ? 'green' : dashboardQuery.data.value?.overview.confidence_level === 'medium' ? 'gold' : 'default') : 'default'">
            {{ selectedDateHasData ? t(`tracking.confidence.${dashboardQuery.data.value?.overview.confidence_level ?? 'low'}`) : '暂无抓取' }}
          </a-tag>
        </div>

        <div class="metric-grid tracking-metric-grid">
          <article v-for="card in selectedMetricCards" :key="card.key" class="metric-card">
            <p class="metric-card__label">{{ card.title }}</p>
            <div class="metric-card__value-row">
              <strong>{{ formatPercent(card.value) }}</strong>
              <span v-if="formatDelta(card.value, card.previous)" :class="['metric-card__delta', (card.value ?? 0) >= (card.previous ?? 0) ? 'metric-card__delta--up' : 'metric-card__delta--down']">
                {{ formatDelta(card.value, card.previous) }}
              </span>
            </div>
            <div class="metric-card__sparkline">
              <span
                v-for="(bucket, index) in dashboardQuery.data.value?.brand_time_buckets ?? []"
                :key="`${card.key}-${bucket.date}`"
                :class="bucketPointClass(bucket, card.points[index])"
              />
            </div>
          </article>
        </div>
      </a-card>

      <a-card class="tracking-panel" :loading="dashboardQuery.isLoading.value">
        <div class="tracking-panel__header">
          <h3 class="panel-title">{{ t("tracking.platformMatrixTitle") }}</h3>
        </div>

        <div class="platform-table">
          <div class="platform-table__head">
            <span>{{ t("tracking.columns.platform") }}</span>
            <span>{{ t("tracking.metrics.mentionRate") }}</span>
            <span>{{ t("tracking.metrics.top1MentionRate") }}</span>
            <span>{{ t("common.status") }}</span>
          </div>

          <div
            v-for="item in dashboardQuery.data.value?.platform_breakdown ?? []"
            :key="item.ai_platform_id"
            class="platform-table__row"
          >
            <div class="platform-table__platform">
              <strong>{{ item.platform_name }}</strong>
              <span>{{ item.actual_sample_count }} {{ t("tracking.samples") }}</span>
            </div>
            <span>{{ item.platform_sample_status === 'sampled' ? formatPercent(item.mention_rate) : '--' }}</span>
            <span>{{ item.platform_sample_status === 'sampled' ? formatPercent(item.top1_mention_rate) : '--' }}</span>
            <a-tag :color="statusTagColor(item.platform_sample_status)">
              {{ statusLabel(item.platform_sample_status) }}
            </a-tag>
          </div>
        </div>
      </a-card>
    </div>

    <a-card class="tracking-panel tracking-panel--ai-platforms" :loading="dashboardQuery.isLoading.value || aiAccountsQuery.isLoading.value">
      <div class="tracking-panel__header">
        <div>
          <h3 class="panel-title">AI 平台状态</h3>
          <span class="tracking-panel__helper">
            只读展示 desktop-client 的绑定、授权和当前采样状态。授权入口放在桌面端，抓取过程静默执行。
          </span>
        </div>
      </div>

      <div class="ai-platform-status-grid">
        <article
          v-for="platform in aiPlatformStatusCards"
          :key="platform.id"
          class="ai-platform-status-card"
        >
          <div class="ai-platform-status-card__head">
            <div class="ai-platform-status-card__identity">
              <span
                class="ai-platform-status-card__badge"
                :style="{ backgroundColor: `${platform.accent}14`, color: platform.accent, borderColor: `${platform.accent}30` }"
              >
                {{ platform.shortName }}
              </span>
              <div>
                <strong>{{ platform.label }}</strong>
                <p>{{ platform.description }}</p>
              </div>
            </div>

            <a-tag :color="accountAuthColor(platform.account)">
              {{ accountAuthLabel(platform.account) }}
            </a-tag>
          </div>

          <div class="ai-platform-status-card__meta">
            <div class="ai-platform-status-card__row">
              <span>绑定状态</span>
              <strong>{{ platform.account ? "已绑定" : "未绑定" }}</strong>
            </div>
            <div class="ai-platform-status-card__row">
              <span>客户端</span>
              <strong>{{ desktopStatusLabel(platform.account) }}</strong>
            </div>
            <div class="ai-platform-status-card__row">
              <span>采样状态</span>
              <a-tag :color="statusTagColor(platform.sampleStatus)">
                {{ statusLabel(platform.sampleStatus) }}
              </a-tag>
            </div>
            <div class="ai-platform-status-card__row">
              <span>最近采样</span>
              <strong>{{ platform.lastSampledAt ? formatDateTime(platform.lastSampledAt) : "--" }}</strong>
            </div>
            <div class="ai-platform-status-card__row">
              <span>样本数</span>
              <strong>{{ formatCount(platform.actualSampleCount) }}</strong>
            </div>
            <div v-if="platform.account" class="ai-platform-status-card__row">
              <span>账号</span>
              <strong>{{ platform.account.display_name }}</strong>
            </div>
          </div>
        </article>
      </div>
    </a-card>

    <div class="tracking-grid tracking-grid--middle">
      <a-card class="tracking-panel" :loading="dashboardQuery.isLoading.value">
        <div class="tracking-panel__header">
          <h3 class="panel-title">{{ t("tracking.coverageTitle") }}</h3>
          <span class="tracking-panel__helper">{{ coverageSummary }}</span>
        </div>

        <a-progress
          :percent="Math.round((dashboardQuery.data.value?.overview.coverage_rate ?? 0) * 100)"
          :show-info="false"
          stroke-color="#f97316"
          trail-color="#fff1e6"
        />

        <div class="tracking-day-row">
          <a-tooltip
            v-for="bucket in dashboardQuery.data.value?.brand_time_buckets ?? []"
            :key="bucket.date"
            :title="`${bucket.date} · ${statusLabel(bucket.sample_status)} · ${bucket.actual_sample_count}/${bucket.planned_sample_count}`"
          >
            <span :class="dayDotClass(bucket)" />
          </a-tooltip>
        </div>
      </a-card>

      <a-card class="tracking-panel" :loading="dashboardQuery.isLoading.value">
        <div class="tracking-panel__header">
          <h3 class="panel-title">{{ t("tracking.hotQuestionsTitle") }}</h3>
          <span class="tracking-panel__helper">{{ t("tracking.hotQuestionsHint") }}</span>
        </div>

        <div v-if="hotQuestions.length" class="question-list">
          <button
            v-for="question in hotQuestions"
            :key="`${question.question_id}-${question.question_hash}`"
            type="button"
            class="question-list__item"
            @click="openQuestion(question)"
          >
            <div>
              <strong>{{ question.question_text }}</strong>
              <p>{{ t("tracking.metrics.mentionRate") }} {{ formatPercent(question.mention_rate) }}</p>
            </div>
            <span class="question-list__time">{{ question.last_sampled_at ? formatDateTime(question.last_sampled_at) : '--' }}</span>
          </button>
        </div>

        <a-empty v-else :description="t('tracking.noQuestions')" />
      </a-card>
    </div>

    <div class="tracking-grid tracking-grid--bottom">
      <a-card class="tracking-panel tracking-panel--citation" :loading="dashboardQuery.isLoading.value">
        <div class="tracking-citation-section">
          <div class="tracking-citation-header">
            <h3 class="tracking-citation-title">{{ t("tracking.citationRankingTitle") }}</h3>
            <p class="tracking-citation-desc">{{ t("tracking.citationRankingHint") }}</p>
          </div>

          <div class="citation-ranking-layout">
            <div class="citation-ranking-visual">
              <div class="citation-ranking-visual__canvas">
                <div class="citation-ranking-visual__circle" :style="citationChartStyle" />
              </div>

              <div v-if="citationRankingRows.length" class="citation-ranking-legend">
                <div
                  v-for="item in citationRankingRows"
                  :key="`legend-${item.ai_platform_id}`"
                  class="citation-ranking-legend__item"
                >
                  <span class="citation-ranking-legend__swatch" :style="{ background: item.color }" />
                  <span class="citation-platform__mini">
                    <span
                      class="citation-platform__mini-badge"
                      :style="{ background: item.surface, color: item.color }"
                    >
                      {{ item.glyph }}
                    </span>
                    <span>{{ item.platform_name }}</span>
                  </span>
                </div>
              </div>

              <a-empty
                v-else
                :description="t('tracking.noCitationRanking')"
              />
            </div>

            <div class="citation-ranking-sheet">
              <div class="citation-ranking-sheet__head">
                <span>{{ t("tracking.columns.platform") }}</span>
                <span>{{ t("tracking.columns.appearances") }}</span>
                <span>{{ t("tracking.columns.citationRate") }}</span>
              </div>

              <div
                v-for="item in citationRankingRows"
                :key="item.ai_platform_id"
                class="citation-ranking-sheet__row"
              >
                <div class="citation-platform__mini">
                  <span
                    class="citation-platform__mini-badge"
                    :style="{ background: item.surface, color: item.color }"
                  >
                    {{ item.glyph }}
                  </span>
                  <strong>{{ item.platform_name }}</strong>
                </div>
                <span class="citation-ranking-sheet__count">{{ formatCount(item.cited_answer_count) }} {{ item.cited_answer_count != null ? '条' : '' }}</span>
                <strong class="citation-ranking-sheet__rate">{{ formatPercent(item.citation_rate) }}</strong>
              </div>

              <a-empty
                v-if="!citationRankingRows.length"
                :description="t('tracking.noCitationRanking')"
              />
            </div>
          </div>
        </div>
      </a-card>

      <a-card class="tracking-panel" :loading="dashboardQuery.isLoading.value">
        <div class="tracking-panel__header">
          <h3 class="panel-title">{{ t("tracking.citedArticlesTitle") }}</h3>
        </div>

        <div class="article-list">
          <div
            v-for="item in visibleCitedArticles"
            :key="item.article_id"
            class="article-list__item"
          >
            <div>
              <strong>{{ item.article_title }}</strong>
              <p>{{ item.publish_platform }}</p>
            </div>
            <div class="article-list__meta">
              <span>{{ item.citation_count }} {{ t("tracking.citations") }}</span>
              <strong>{{ formatPercent(item.citation_rate) }}</strong>
            </div>
          </div>
          <a-empty
            v-if="!visibleCitedArticles.length"
            :description="t('tracking.noCitedArticles')"
          />
        </div>
      </a-card>
    </div>

  </section>
</template>

<style scoped>
.tracking-view {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.tracking-actions-wrap {
  align-items: center;
}

.tracking-view__alert {
  margin-bottom: 4px;
}

.tracking-action-item {
  display: flex;
  align-items: center;
  gap: 8px;
}

.tracking-action-label {
  font-size: 14px;
  color: #1f2937;
  font-weight: 500;
}

.tracking-select {
  min-width: 180px;
}

.tracking-date-picker {
  min-width: 168px;
}


.tracking-grid {
  display: grid;
  gap: 20px;
}

.tracking-grid--top {
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
}

.tracking-grid--middle {
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
}

.tracking-grid--bottom {
  grid-template-columns: minmax(0, 1fr);
}

.tracking-panel--ai-platforms {
  overflow: hidden;
}

.ai-platform-status-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
  margin-top: 12px;
}

.ai-platform-status-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px;
  border-radius: 16px;
  border: 1px solid #e6edf5;
  background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
}

.ai-platform-status-card__head {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  align-items: flex-start;
}

.ai-platform-status-card__identity {
  display: flex;
  gap: 12px;
  align-items: flex-start;
}

.ai-platform-status-card__identity strong {
  display: block;
  color: #111827;
  font-size: 16px;
  line-height: 1.2;
}

.ai-platform-status-card__identity p {
  margin: 6px 0 0;
  color: #667085;
  line-height: 1.5;
  font-size: 12px;
}

.ai-platform-status-card__badge {
  width: 42px;
  height: 42px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 14px;
  border: 1px solid transparent;
  font-size: 16px;
  font-weight: 700;
  flex-shrink: 0;
}

.ai-platform-status-card__meta {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ai-platform-status-card__row {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  align-items: center;
  padding: 10px 12px;
  border-radius: 12px;
  background: #f8fafc;
}

.ai-platform-status-card__row span {
  color: #667085;
  font-size: 12px;
}

.ai-platform-status-card__row strong {
  color: #111827;
  font-size: 13px;
  text-align: right;
  word-break: break-all;
}

.tracking-citation-section {
  display: flex;
  flex-direction: column;
  padding: 0;
  margin-top: 10px;
}

.tracking-citation-section.is-loading {
  opacity: 0.6;
  pointer-events: none;
}

.tracking-citation-header {
  margin-bottom: 24px;
}

.tracking-citation-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: #1a1a1a;
}

.tracking-citation-desc {
  margin: 4px 0 0;
  color: #8c8c8c;
  font-size: 13px;
}

.tracking-panel {
  border-radius: 12px;
  background: #ffffff;
}

.tracking-panel--citation {
  overflow: hidden;
}

.tracking-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

.tracking-panel__header-left {
  display: flex;
  flex-direction: column;
}

.panel-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: #1a1a1a;
  display: flex;
  align-items: center;
}

.panel-title::before {
  content: "";
  display: inline-block;
  width: 4px;
  height: 14px;
  background: #1f5cff;
  border-radius: 2px;
  margin-right: 8px;
}

.tracking-panel__description {
  margin: 10px 0 0 12px;
  color: #a3a3a3;
  font-size: 13px;
}

.tracking-panel__helper {
  color: var(--muted);
  font-size: 13px;
}

.tracking-metric-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px;
}

.metric-card {
  background: #f7f9fc;
  border: 1px solid #f0f3fa;
  border-radius: 12px;
  padding: 16px;
}

.metric-card__label {
  margin: 0;
  color: #8c8c8c;
  font-size: 13px;
}

.metric-card__value-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 10px;
}

.metric-card__value-row strong {
  font-size: 24px;
  font-weight: 800;
  color: #141414;
}

.metric-card__delta {
  font-size: 13px;
  font-weight: 700;
}

.metric-card__delta--up {
  color: #15803d;
}

.metric-card__delta--down {
  color: #dc2626;
}

.metric-card__sparkline {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 22px;
  margin-top: 14px;
}

.metric-card__point {
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: #1677ff;
}

.metric-card__point--partial {
  opacity: 0.4;
}

.metric-card__point--empty {
  background: transparent;
  border: 1px solid #1677ff;
}

.platform-table,
.citation-table {
  display: flex;
  flex-direction: column;
  gap: 0;
}

.platform-table__head,
.platform-table__row,
.citation-table__head,
.citation-table__row {
  display: grid;
  grid-template-columns: minmax(0, 1.5fr) repeat(3, minmax(0, 1fr));
  align-items: center;
  gap: 12px;
}

.platform-table__head,
.citation-table__head {
  background: #fafafa;
  color: #8c8c8c;
  font-weight: 500;
  font-size: 13px;
  padding: 12px 16px;
  border-bottom: 1px solid #f0f0f0;
}

.platform-table__row,
.citation-table__row {
  padding: 16px;
  border-bottom: 1px solid #f9f9f9;
  background: #ffffff;
}

.platform-table__row:last-child,
.citation-table__row:last-child {
  border-bottom: none;
}

.platform-table__platform span {
  display: block;
  margin-top: 4px;
  color: var(--muted);
  font-size: 12px;
}

.citation-ranking-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(400px, 1fr);
  gap: 40px;
  align-items: flex-start;
}

.citation-ranking-visual {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 32px;
  padding: 0;
}

.citation-ranking-visual__canvas {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
}

.citation-ranking-visual__circle {
  width: min(32vw, 420px);
  height: min(32vw, 420px);
  min-width: 280px;
  min-height: 280px;
  border-radius: 999px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.85);
}

.citation-ranking-legend {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 16px 24px;
}

.citation-ranking-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: #4b5563;
  font-size: 14px;
}

.citation-ranking-legend__swatch {
  width: 12px;
  height: 12px;
  border-radius: 4px;
  flex: 0 0 auto;
}

.citation-platform__mini {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.citation-platform__mini-badge {
  display: inline-flex;
  width: 20px;
  height: 20px;
  border-radius: 999px;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  flex: 0 0 auto;
}

.citation-ranking-sheet {
  border-radius: 12px;
  border: 1px solid #f0f3fa;
  background: #ffffff;
  overflow: hidden;
}

.citation-ranking-sheet__head,
.citation-ranking-sheet__row {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 0.8fr) minmax(0, 0.8fr);
  align-items: center;
  gap: 16px;
  padding: 20px 32px;
}

.citation-ranking-sheet__head {
  background: #fafafa;
  color: #4b5563;
  font-size: 13px;
  font-weight: 500;
  border-bottom: 1px solid #f0f0f0;
}

.citation-ranking-sheet__row {
  border-bottom: 1px solid #f9f9f9;
  color: #4b5563;
  font-size: 14px;
}

.citation-ranking-sheet__row:last-child {
  border-bottom: none;
}

.citation-ranking-sheet__row strong {
  color: #111827;
  font-size: 14px;
}

.citation-ranking-sheet__row > span,
.citation-ranking-sheet__row > strong {
  justify-self: start;
}

.citation-ranking-sheet__count {
  color: #374151;
}

.citation-ranking-sheet__rate {
  color: #111827;
}

.tracking-day-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 18px;
}

.tracking-day-dot {
  width: 12px;
  height: 12px;
  border-radius: 999px;
  border: 1px solid #fdba74;
  background: transparent;
}

.tracking-day-dot--sampled {
  background: #f97316;
  border-color: #f97316;
}

.tracking-day-dot--partial {
  background: linear-gradient(90deg, #f97316 50%, transparent 50%);
}
.question-list,
.article-list,
.detail-citation-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.question-list__item,
.article-list__item,
.detail-citation-list__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 16px;
  border: 1px solid #f0f3fa;
  border-radius: 12px;
  background: #f7f9fc;
  text-align: left;
  transition: all 0.2s;
}

.question-list__item:hover,
.article-list__item:hover,
.detail-citation-list__item:hover {
  background: #ffffff;
  border-color: #e5eaf2;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}

.question-list__item {
  cursor: pointer;
}

.question-list__item strong,
.article-list__item strong,
.detail-citation-list__item strong {
  display: block;
}

.question-list__item p,
.article-list__item p,
.detail-citation-list__item p {
  margin: 4px 0 0;
  color: var(--muted);
}

.question-list__time,
.article-list__meta {
  color: var(--muted);
  font-size: 12px;
}

.article-list__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
}

.detail-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 16px;
}

.detail-tabs__item {
  padding: 8px 14px;
  border: 1px solid #fed7aa;
  border-radius: 999px;
  background: #fff7ed;
  cursor: pointer;
}

.detail-tabs__item--active {
  border-color: #f97316;
  background: #f97316;
  color: #fff;
}

.detail-tabs__item--muted {
  opacity: 0.55;
}

.detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 16px;
}

.detail-grid--bottom {
  margin-top: 16px;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

.detail-meta {
  display: flex;
  gap: 12px;
  margin-top: 16px;
  color: var(--muted);
  font-size: 12px;
}

@media (max-width: 1080px) {
  .tracking-grid--top,
  .tracking-grid--middle,
  .tracking-grid--bottom,
  .detail-grid,
  .detail-grid--bottom {
    grid-template-columns: minmax(0, 1fr);
  }

  .citation-ranking-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .citation-ranking-visual {
    min-height: auto;
    padding: 0;
  }

  .citation-ranking-visual__canvas {
    min-height: 280px;
  }

  .citation-ranking-visual__circle {
    width: min(72vw, 360px);
    height: min(72vw, 360px);
  }

  .citation-ranking-sheet__head,
  .citation-ranking-sheet__row {
    padding: 22px 20px;
  }
}

@media (max-width: 640px) {
  .citation-ranking-sheet__head,
  .citation-ranking-sheet__row {
    grid-template-columns: minmax(0, 1.3fr) minmax(0, 0.7fr) minmax(0, 0.7fr);
    gap: 10px;
    font-size: 13px;
  }

  .citation-ranking-sheet__row strong,
  .citation-ranking-sheet__row {
    font-size: 14px;
  }

  .citation-ranking-legend {
    gap: 12px 18px;
    padding-left: 0;
  }
}
</style>
