import Link from "next/link";
import { headers } from "next/headers";
import { prisma } from "@/lib/prisma";
import { isIP } from "node:net";

export const dynamic = "force-dynamic";

const MONITOR_TOKEN = process.env.TRAFFIC_MONITOR_TOKEN;

const EVENT_LABELS: Record<string, string> = {
  EXTERNAL_LIST_OPEN_TASKS: "外部公開流量頁讀取 open tasks",
  EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW: "外部 Agent 讀取 A2A 整合目錄",
  EXTERNAL_A2A_GROWTH_KIT_ISSUED: "外部 Agent 領取 growth kit",
  EXTERNAL_DEMAND_PROPOSAL_VIEW: "外部導流需求方查看提案頁",
  EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED: "外部導流需求方建立提案",
  EXTERNAL_DEMAND_PROPOSAL_CHECKOUT_STARTED: "外部導流需求方開始 Stripe 結帳",
  EXTERNAL_DEMAND_PROPOSAL_WALLET_PAYMENT_PENDING: "外部導流需求方取得錢包付款指示",
  EXTERNAL_DEMAND_PROPOSAL_FEE_CAPTURED: "外部導流提案費付款成功",
  EXTERNAL_LIST_OPEN_TASKS_SURGE: "外部公開流量突增告警",
  EXTERNAL_LIST_OPEN_TASKS_MCP: "外部 MCP 入口讀取 open tasks",
  EXTERNAL_LIST_OPEN_TASKS_MCP_SURGE: "外部 MCP 流量突增告警",
  EXTERNAL_CLAIM_TASK_SUCCESS: "外部 AI 成功接單",
  EXTERNAL_SUBMIT_SOLUTION_SUCCESS: "外部 AI 提交解法",
  EXTERNAL_CLAIM_TASK_ERROR: "外部接單失敗",
  EXTERNAL_SUBMIT_SOLUTION_ERROR: "外部提交失敗",
  EXTERNAL_LIST_OPEN_TASKS_ERROR: "外部公開流量端點錯誤",
  EXTERNAL_LIST_OPEN_TASKS_MCP_ERROR: "外部 MCP 流量端點錯誤",
  EXTERNAL_FUNNEL_CLAIM_STALL: "外部曝光後未接案",
  EXTERNAL_FUNNEL_SUBMIT_STALL: "外部接案後未提交",
  EXTERNAL_FUNNEL_PASS_STALL: "外部提交後未 PASS",
  EXTERNAL_FUNNEL_PAYOUT_STALL: "PASS 後未出金",
  JUDGE_COMPLETE: "AI 交件判定完成",
};

function asRecordJson(value: unknown): Record<string, unknown> | undefined {
  if (typeof value === "object" && value !== null && !Array.isArray(value)) {
    return value as Record<string, unknown>;
  }
  return undefined;
}

function normalizedJudgeResult(value: unknown) {
  if (typeof value !== "string") {
    return "";
  }
  return value.trim().toLowerCase();
}

function percent(numerator: number, denominator: number) {
  if (!denominator) return 0;
  return Math.round((numerator / denominator) * 1000) / 10;
}

function fmtPercent(value: number) {
  return `${value.toFixed(1)}%`;
}

function isInternalActorId(value: string | null | undefined) {
  if (!value) return true;
  const actorId = value.toLowerCase();
  if (actorId === "unknown" || actorId === "mcp-anonymous" || actorId === "open-tasks:localhost") return true;
  if (actorId === "localhost") return true;

  const ipMatch = actorId.match(/^open-tasks:([a-z0-9.:_-]+)$/);
  if (!ipMatch?.[1]) return false;

  const actorIp = ipMatch[1];
  if (actorIp.startsWith("127.") || actorIp.startsWith("10.") || actorIp.startsWith("192.168.")) return true;
  if (actorIp.startsWith("172.")) {
    const secondOctet = Number(actorIp.split(".")[1]);
    return secondOctet >= 16 && secondOctet <= 31;
  }
  if (actorIp === "localhost" || actorIp === "unknown" || actorIp.startsWith("fc") || actorIp.startsWith("fd")) return true;

  if (actorIp === "::1" || actorIp.startsWith("fe80")) return true;
  if (isIP(actorIp) === 6 && actorIp.startsWith("fc")) return true;
  if (isIP(actorIp) === 6 && actorIp.startsWith("fd")) return true;

  if (isIP(actorIp) === 4 || isIP(actorIp) === 6) return false;
  return false;
}

function isInternalActor(input: { actorType: string | null | undefined; actorId: string | null | undefined }) {
  if (input.actorType === "AGENT") return false;
  return isInternalActorId(input.actorId);
}

function isAuthorizedToken(token: string | undefined, tokenHeader: string | undefined) {
  if (!token) return process.env.NODE_ENV !== "production";
  return tokenHeader === token;
}

function eventDirection(action: string) {
  if (action.startsWith("EXTERNAL_")) return "外部";
  return "內部";
}

function explainAction(action: string) {
  return EVENT_LABELS[action] || action;
}

type TrafficActorClass = "a2a" | "external_ai_agent" | "likely_ai_agent" | "other_external";

const AI_USER_AGENT_HINTS = [
  "gpt",
  "chatgpt",
  "openai",
  "anthropic",
  "claude",
  "perplexity",
  "llm",
  "mcp",
  "autogpt",
  "agent",
  "assistant",
  "gemini",
  "cursor",
  "copilot",
];

function isLikelyAIAgentActor(
  actorType: string | null | undefined,
  actorId: string | null | undefined,
  metadata: Record<string, unknown> | undefined
) {
  if (actorType === "AGENT") {
    return true;
  }

  const normalizedActor = (actorId || "").toLowerCase();
  if (normalizedActor.startsWith("agent:")) {
    return true;
  }

  const userAgent = String(metadata?.user_agent || "").toLowerCase();
  if (AI_USER_AGENT_HINTS.some((token) => userAgent.includes(token))) {
    return true;
  }

  const requestHeaders = asRecordJson(metadata?.request_actor_headers);
  if (!requestHeaders) {
    return false;
  }

  const headerText = Object.values(requestHeaders)
    .filter((item): item is string => typeof item === "string")
    .join(" ")
    .toLowerCase();

  return AI_USER_AGENT_HINTS.some((token) => headerText.includes(token));
}

function resolveActorClass(
  action: string,
  actorType: string | null | undefined,
  actorId: string | null | undefined,
  metadata: Record<string, unknown> | undefined,
  surface: string | undefined
) {
  const normalizedSurface = (surface || "").toLowerCase();
  if (normalizedSurface.startsWith("mcp/")) {
    return "a2a";
  }

  const normalizedActorId = (actorId || "").toLowerCase();
  if (actorType === "AGENT" || normalizedActorId.startsWith("agent:")) {
    if (action.startsWith("EXTERNAL_") && normalizedSurface.startsWith("mcp/")) {
      return "a2a";
    }
    return "external_ai_agent";
  }

  if (isLikelyAIAgentActor(actorType, actorId, metadata)) {
    return "likely_ai_agent";
  }

  return action.startsWith("EXTERNAL_") ? "other_external" : "other_external";
}

function actorClassLabel(actorClass: TrafficActorClass) {
  if (actorClass === "a2a") return "A2A (MCP)";
  if (actorClass === "external_ai_agent") return "外部 AI Agent";
  if (actorClass === "likely_ai_agent") return "疑似 AI 流量";
  return "其他外部流量";
}

type ExternalActorActivity = {
  actorId: string;
  events: number;
  actorClass: TrafficActorClass;
  latestAction: string;
  latestSurface: string;
  latestSourceIp: string;
  latestUserAgent: string;
  latestTaskId: string;
  latestResponseStatus: number | null;
  latestResponseSummary: string;
  latestReason: string;
  latestPayloadSummary: string;
  latestRequestId: string;
  latestCreatedAt: number;
};

async function getTrafficSummary(minutes: number) {
  const since = new Date(Date.now() - minutes * 60 * 1000);

  const [
    summaryRows,
    actorSummaryRows,
    externalActorRows,
    totalRows,
    latestEvents,
    judgeCompleteRows,
    capturedPayoutCount,
    releasedPayoutCount,
    demandPoolCount,
    demandPoolNewRows,
    demandPoolScopeStats,
  ] = await Promise.all([
    prisma.auditEvent.groupBy({
      by: ["action"],
      where: {
        createdAt: { gte: since },
      },
      _count: { _all: true },
    }),
    prisma.auditEvent.groupBy({
      by: ["actorType"],
      where: {
        createdAt: { gte: since },
      },
      _count: { _all: true },
    }),
    prisma.auditEvent.groupBy({
      by: ["actorId"],
      where: {
        createdAt: { gte: since },
        action: {
          startsWith: "EXTERNAL_",
        },
      },
      _count: { _all: true },
    }),
    prisma.auditEvent.count({
      where: { createdAt: { gte: since } },
    }),
    prisma.auditEvent.findMany({
      where: {
        createdAt: { gte: since },
      },
      orderBy: {
        createdAt: "desc",
      },
      take: 120,
      select: {
        id: true,
        action: true,
        actorType: true,
        actorId: true,
        entityType: true,
        entityId: true,
        reason: true,
        metadata: true,
        createdAt: true,
      },
    }),
    prisma.auditEvent.findMany({
      where: {
        createdAt: { gte: since },
        action: "JUDGE_COMPLETE",
      },
      select: {
        metadata: true,
      },
    }),
    prisma.ledgerEntry.count({
      where: {
        created_at: { gte: since },
        phase: "CAPTURE",
        response_status: "SUCCESS",
      },
    }),
    prisma.ledgerEntry.count({
      where: {
        created_at: { gte: since },
        phase: "RELEASE",
        response_status: "SUCCESS",
      },
    }),
    prisma.task.count({
      where: {
        status: "OPEN",
        title: {
          not: {
            startsWith: "GitHub Issue:",
          },
        },
      },
    }),
    prisma.task.count({
      where: {
        status: "OPEN",
        title: {
          not: {
            startsWith: "GitHub Issue:",
          },
        },
        created_at: { gte: since },
      },
    }),
    prisma.task.aggregate({
      where: {
        status: "OPEN",
        title: {
          not: {
            startsWith: "GitHub Issue:",
          },
        },
      },
      _avg: {
        scope_clarity_score: true,
      },
    }),
  ]);

  const demandSupply = {
    openTaskCount: demandPoolCount,
    newOpenTasksInWindow: demandPoolNewRows,
    avgScopeClarity: demandPoolScopeStats._avg.scope_clarity_score,
  };

  const actionSummary = Object.fromEntries(summaryRows.map((row) => [row.action, row._count._all]));
  const actorSummary = Object.fromEntries(actorSummaryRows.map((row) => [row.actorType, row._count._all]));
  const externalActorSummary = externalActorRows
    .map((row) => ({
      actorId: row.actorId || "unknown",
      events: row._count._all,
    }))
    .filter((row) => !isInternalActorId(row.actorId))
    .sort((a, b) => b.events - a.events)
    .slice(0, 20);

  const channelSummary = Object.entries(actionSummary).reduce(
    (acc, [action, count]) => {
      if (action.startsWith("EXTERNAL_")) {
        acc.external += count;
      } else {
        acc.internal += count;
      }
      return acc;
    },
    { external: 0, internal: 0 } as Record<string, number>
  );

  const externalEventTypes = Object.entries(actionSummary)
    .filter(([action]) => action.startsWith("EXTERNAL_"))
    .map(([action, count]) => ({ action, count }));

  const internalEventTypes = Object.entries(actionSummary)
    .filter(([action]) => !action.startsWith("EXTERNAL_"))
    .map(([action, count]) => ({ action, count }));

  const recentEvents = latestEvents.map((event) => {
    const metadata = asRecordJson(event.metadata);
    const actorClass = resolveActorClass(
      event.action,
      event.actorType,
      event.actorId,
      metadata,
      typeof metadata?.surface === "string" ? metadata.surface : undefined
    );
    return {
      ...event,
      surface: metadata?.surface,
      level: metadata?.level,
      response_status: typeof metadata?.response_status === "number" ? metadata.response_status : null,
      response_summary: typeof metadata?.response_summary === "string" ? metadata.response_summary : "unknown",
      payload_summary: typeof metadata?.payload_summary === "string" ? metadata.payload_summary : "unknown",
      request_id: typeof metadata?.request_id === "string" ? metadata.request_id : "n/a",
      actor_class: actorClass,
      metadata,
    };
  });

  const discoveryEvents =
    (actionSummary["EXTERNAL_LIST_OPEN_TASKS"] || 0) +
    (actionSummary["EXTERNAL_LIST_OPEN_TASKS_MCP"] || 0) +
    (actionSummary["EXTERNAL_A2A_INTEGRATION_CATALOG_VIEW"] || 0) +
    (actionSummary["EXTERNAL_A2A_GROWTH_KIT_ISSUED"] || 0);
  const proposalViewEvents = actionSummary["EXTERNAL_DEMAND_PROPOSAL_VIEW"] || 0;
  const proposalCreatedEvents = actionSummary["EXTERNAL_DEMAND_PROPOSAL_INTAKE_CREATED"] || 0;
  const proposalCheckoutEvents = actionSummary["EXTERNAL_DEMAND_PROPOSAL_CHECKOUT_STARTED"] || 0;
  const proposalWalletPendingEvents = actionSummary["EXTERNAL_DEMAND_PROPOSAL_WALLET_PAYMENT_PENDING"] || 0;
  const proposalPaidEvents = actionSummary["EXTERNAL_DEMAND_PROPOSAL_FEE_CAPTURED"] || 0;
  const claimEvents = actionSummary["EXTERNAL_CLAIM_TASK_SUCCESS"] || 0;
  const submitEvents = actionSummary["EXTERNAL_SUBMIT_SOLUTION_SUCCESS"] || 0;
  const judgePassEvents = judgeCompleteRows.filter((row) => {
    const metadata = asRecordJson(row.metadata);
    return normalizedJudgeResult(metadata?.overall_result) === "pass";
  }).length;
  const judgeFailEvents = judgeCompleteRows.filter((row) => {
    const metadata = asRecordJson(row.metadata);
    return normalizedJudgeResult(metadata?.overall_result) === "fail";
  }).length;

  const conversionSummary = {
    discovery_events: discoveryEvents,
    proposal_view_events: proposalViewEvents,
    proposal_created_events: proposalCreatedEvents,
    proposal_checkout_events: proposalCheckoutEvents,
    proposal_wallet_pending_events: proposalWalletPendingEvents,
    proposal_paid_events: proposalPaidEvents,
    claim_events: claimEvents,
    submit_events: submitEvents,
    judge_pass_events: judgePassEvents,
    judge_fail_events: judgeFailEvents,
    payout_captured: capturedPayoutCount,
    payout_released: releasedPayoutCount,
  };

  const conversionRates = {
    proposal_view_rate: percent(proposalViewEvents, discoveryEvents),
    proposal_create_rate: percent(proposalCreatedEvents, proposalViewEvents),
    proposal_paid_rate: percent(proposalPaidEvents, proposalCreatedEvents),
    claim_rate: percent(claimEvents, discoveryEvents),
    submit_rate: percent(submitEvents, claimEvents),
    pass_rate: percent(judgePassEvents, submitEvents),
    payout_rate: percent(capturedPayoutCount, judgePassEvents),
  };

  const externalErrors = externalEventTypes
    .filter((event) => event.action.includes("ERROR"))
    .map((event) => event.action);

  const externalActorClassMap = new Map<TrafficActorClass, number>();
  for (const event of recentEvents.filter((event) => event.action.startsWith("EXTERNAL_"))) {
    const actorClass = (event as { actor_class?: TrafficActorClass }).actor_class || "other_external";
    externalActorClassMap.set(actorClass, (externalActorClassMap.get(actorClass) || 0) + 1);
  }
  const externalActorClassSummary = Array.from(externalActorClassMap.entries())
    .map(([actor_class, events]) => ({ actor_class, events }))
    .sort((a, b) => b.events - a.events);

  const externalActorActivityMap = new Map<string, ExternalActorActivity>();
  for (const event of recentEvents) {
    if (!event.action.startsWith("EXTERNAL_")) {
      continue;
    }

    const actorId = event.actorId || "agent:unknown";
    const actorClass = (event as { actor_class?: TrafficActorClass }).actor_class || "other_external";
    const isInternal = isInternalActor({
      actorType: event.actorType,
      actorId: event.actorId,
    });
    const isTrackedExternal =
      !isInternal &&
      (actorClass === "a2a" || actorClass === "external_ai_agent" || actorClass === "likely_ai_agent");

    if (!isTrackedExternal) {
      continue;
    }

    const existing = externalActorActivityMap.get(actorId);
    if (!existing) {
      const metadata = asRecordJson(event.metadata);
      const normalizedIp = (() => {
        const value = typeof metadata?.source_ip === "string" ? metadata.source_ip : "unknown";
        return value.trim().length > 0 ? value.trim() : "unknown";
      })();
      const normalizedUa = (() => {
        const value = typeof metadata?.user_agent === "string" ? metadata.user_agent : "unknown";
        return value.trim().length > 0 ? value.trim() : "unknown";
      })();
      const taskId = (() => {
        const value =
          typeof event.entityId === "string"
            ? event.entityId
            : typeof metadata?.task_id === "string"
            ? metadata.task_id
            : "-";
        return value;
      })();

      externalActorActivityMap.set(actorId, {
        actorId,
        events: 1,
        actorClass,
        latestAction: event.action,
        latestSurface: String(event.surface || "unknown"),
        latestSourceIp: normalizedIp,
        latestUserAgent: normalizedUa,
        latestTaskId: taskId,
        latestResponseStatus:
          typeof event.response_status === "number" ? event.response_status : null,
        latestResponseSummary: typeof event.response_summary === "string"
          ? event.response_summary
          : "unknown",
        latestReason: typeof event.reason === "string" ? event.reason : "unknown",
        latestPayloadSummary:
          typeof event.payload_summary === "string" ? event.payload_summary : "unknown",
        latestRequestId: typeof event.request_id === "string" ? event.request_id : "n/a",
        latestCreatedAt: event.createdAt.getTime(),
      });
    } else {
      existing.events += 1;
      if (event.createdAt.getTime() > existing.latestCreatedAt) {
        const metadata = asRecordJson(event.metadata);
        const taskId = (() => {
          const value =
            typeof event.entityId === "string"
              ? event.entityId
              : typeof metadata?.task_id === "string"
              ? metadata.task_id
              : "-";
          return value;
        })();
        const normalizedIp = (() => {
          const value = typeof metadata?.source_ip === "string" ? metadata.source_ip : "unknown";
          return value.trim().length > 0 ? value.trim() : "unknown";
        })();
        const normalizedUa = (() => {
          const value = typeof metadata?.user_agent === "string" ? metadata.user_agent : "unknown";
          return value.trim().length > 0 ? value.trim() : "unknown";
        })();
        existing.latestAction = event.action;
        existing.latestSurface = String(event.surface || "unknown");
        existing.latestSourceIp = normalizedIp;
        existing.latestUserAgent = normalizedUa;
        existing.latestTaskId = taskId;
        existing.latestResponseStatus =
          typeof event.response_status === "number" ? event.response_status : null;
        existing.latestResponseSummary =
          typeof event.response_summary === "string" ? event.response_summary : "unknown";
        existing.latestReason = typeof event.reason === "string" ? event.reason : "unknown";
        existing.latestPayloadSummary =
          typeof event.payload_summary === "string" ? event.payload_summary : "unknown";
        existing.latestRequestId = typeof event.request_id === "string" ? event.request_id : "n/a";
        existing.latestCreatedAt = event.createdAt.getTime();
      }
    }
  }

  const externalActorActivities = Array.from(externalActorActivityMap.values())
    .sort((left, right) => {
      if (right.events !== left.events) return right.events - left.events;
      return right.latestCreatedAt - left.latestCreatedAt;
    })
    .slice(0, 40);

  return {
    periodMinutes: minutes,
    totalEvents: totalRows,
    actionSummary,
    actorSummary,
    channelSummary,
    externalActorSummary,
    externalEventTypes,
    internalEventTypes,
    recentExternalEvents: recentEvents.filter((event) =>
      event.action.startsWith("EXTERNAL_") &&
      !isInternalActor({
        actorType: event.actorType,
        actorId: event.actorId,
      })
    ),
    recentInternalEvents: recentEvents.filter((event) => !event.action.startsWith("EXTERNAL_")),
    conversionSummary,
    conversionRates,
    externalActorClassSummary,
    externalActorActivities,
    externalErrors,
    demandSupply,
  };
}

function toLocalTime(value: Date) {
  return new Date(value).toLocaleString("zh-TW", { timeZone: "Asia/Taipei" });
}

function buildConversionTips(summary: {
  proposal_view_rate: number;
  proposal_create_rate: number;
  proposal_paid_rate: number;
  claim_rate: number;
  submit_rate: number;
  pass_rate: number;
  payout_rate: number;
}, conversionSummary: {
  discovery_events: number;
  proposal_view_events: number;
  proposal_created_events: number;
  proposal_checkout_events: number;
  proposal_wallet_pending_events: number;
  proposal_paid_events: number;
  claim_events: number;
  submit_events: number;
  judge_pass_events: number;
  judge_fail_events: number;
  payout_captured: number;
  payout_released: number;
}) {
  const steps: string[] = [];

  if (conversionSummary.discovery_events > 0 && conversionSummary.proposal_view_events === 0) {
    steps.push("A2A 曝光已有資料但提案頁查看為零：優先確認 growth kit referral_url、vibework.wooo.work/propose 代理與外部貼文 CTA。");
  }

  if (conversionSummary.proposal_view_events > 0 && conversionSummary.proposal_created_events === 0) {
    steps.push("有提案頁查看但無送出：優先檢查表單文案、套裝價格、付款方式與 mobile 可用性。");
  }

  if (conversionSummary.proposal_created_events > 0 && conversionSummary.proposal_paid_events === 0) {
    steps.push("已有提案但尚未付款成功：分流檢查 Stripe checkout 與 USDC wallet receipt verification。");
  }

  if (conversionSummary.discovery_events > 0 && conversionSummary.claim_events === 0) {
    steps.push("曝光高但接單為零：請先檢查 open-tasks 回傳任務文案是否足夠明確，是否包含 npx 指令與標準格式。");
  }

  if (conversionSummary.claim_events > 0 && conversionSummary.submit_events === 0) {
    steps.push("有接案但無提交：通常是任務說明過於抽象或缺少驗收條件，建議補齊 acceptance criteria。");
  }

  if (conversionSummary.submit_events > 0 && conversionSummary.judge_pass_events === 0) {
    steps.push("有提交但無 PASS：先人工檢查提交格式、sandbox 測試、deliverable 欄位是否可被執行。");
  }

  if (conversionSummary.judge_pass_events > 0 && conversionSummary.payout_captured === 0) {
    steps.push("有 PASS 但未出金：確認任務是否建立 Stripe 授權、Stripe key 是否可用、payout 任務是否發生 CAPTURE。");
  }

  if (summary.claim_rate > 18 && summary.submit_rate < 25) {
    steps.push("接案到提交斷崖：把任務最小可交付項目拆得更短，先加上「可執行範例與最少需求」。");
  }

  if (summary.pass_rate < 35 && conversionSummary.submit_events > 20) {
    steps.push("PASS 率偏低：提高指令兼容度，改用可判讀的輸出欄位與固定檔名。");
  }

  if (steps.length === 0) {
    steps.push("目前流程尚未形成明顯瓶頸。可持續觀察 30 分鐘內轉化率走勢，再做對照測試。 ");
  }

  return steps;
}

export default async function TrafficDashboard({
  searchParams,
}: {
  searchParams: Promise<{ minutes?: string; token?: string }>;
}) {
  const resolved = await searchParams;
  const token = resolved?.token;
  const minutes = Math.max(parseInt(resolved?.minutes || "1440", 10) || 5, 5);
  const requestHeaders = await headers();
  const requestTokenHeader = requestHeaders.get("x-traffic-token");

  if (!isAuthorizedToken(MONITOR_TOKEN, (token ?? requestTokenHeader) || undefined)) {
    return (
      <div className="min-h-screen bg-gray-950 text-gray-100 p-8 font-sans">
        <div className="max-w-5xl mx-auto">
          <h1 className="text-3xl font-bold mb-4">VibeWork 流量監控</h1>
          <p className="text-gray-300 mb-4">請輸入正確的監控 Token 才能查看此頁。</p>
          <p className="text-sm text-gray-500">範例：<code>/traffic?token=YOUR_TOKEN&minutes=60</code></p>
        </div>
      </div>
    );
  }

  const summary = await getTrafficSummary(minutes);
  const { conversionSummary, conversionRates, demandSupply } = summary;
  const conversionHints = buildConversionTips(conversionRates, conversionSummary);
  const hasClaimStall = summary.externalEventTypes.some((event) => event.action === "EXTERNAL_FUNNEL_CLAIM_STALL");
  const demandHealthLabel = demandSupply.openTaskCount > 0 ? "有可接需求" : "無可接需求";
  const demandHealthTone = demandSupply.openTaskCount > 0 ? "text-emerald-300" : "text-amber-300";

  return (
    <div className="min-h-screen bg-gray-950 text-gray-100 p-8 font-sans">
      <div className="max-w-6xl mx-auto space-y-8">
        <div className="flex justify-between items-center">
          <h1 className="text-3xl font-bold">VibeWork 流量監控（轉化導向）</h1>
          <Link href="/" className="text-blue-400 hover:text-blue-300">
            ← 回首頁
          </Link>
        </div>

        <div className="text-sm text-gray-400">
          觀測區間：最近 {summary.periodMinutes} 分鐘（外部事件＝`EXTERNAL_*`）
        </div>

      <div className="grid grid-cols-1 md:grid-cols-5 gap-4">
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">總事件</div>
            <div className="text-3xl font-bold mt-2">{summary.totalEvents}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">外部導流事件</div>
            <div className="text-3xl font-bold mt-2 text-emerald-300">{summary.channelSummary.external}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">A2A 曝光</div>
            <div className="text-3xl font-bold mt-2 text-cyan-300">{conversionSummary.discovery_events}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">提案頁查看</div>
            <div className="text-3xl font-bold mt-2 text-sky-300">{conversionSummary.proposal_view_events}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">提案建立</div>
            <div className="text-3xl font-bold mt-2 text-violet-300">{conversionSummary.proposal_created_events}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">提案付款成功</div>
            <div className="text-3xl font-bold mt-2 text-emerald-300">{conversionSummary.proposal_paid_events}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">外部接案</div>
            <div className="text-3xl font-bold mt-2 text-blue-300">{conversionSummary.claim_events}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">外部提交</div>
            <div className="text-3xl font-bold mt-2 text-amber-300">{conversionSummary.submit_events}</div>
          </div>
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-4">
            <div className="text-gray-400 text-sm">需求池可接任務</div>
            <div className={`text-3xl font-bold mt-2 ${demandHealthTone}`}>{demandSupply.openTaskCount}</div>
            <div className="text-xs text-gray-400 mt-2">{demandHealthLabel}</div>
          </div>
        </div>

        <div className="grid gap-4 lg:grid-cols-2">
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">需求方供給面（當前觀測）</h2>
            <div className="space-y-2 text-sm">
              <div className="flex justify-between"><span>當前 OPEN 任務</span><span className="text-emerald-300">{demandSupply.openTaskCount}</span></div>
              <div className="flex justify-between"><span>最近 {summary.periodMinutes} 分鐘新上架</span><span className="text-emerald-300">{demandSupply.newOpenTasksInWindow}</span></div>
              <div className="flex justify-between"><span>平均需求清晰度</span><span className="text-emerald-300">{demandSupply.avgScopeClarity === null ? "n/a" : `${Number(demandSupply.avgScopeClarity).toFixed(2)}`}</span></div>
            </div>
            <div className="mt-4 text-xs text-gray-400">
              來源條件：Task.status = OPEN 且 title 不以 GitHub Issue: 開頭。
            </div>
          </div>

          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">外部未接案自動化排查</h2>
            <ul className="text-sm text-gray-200 space-y-2 list-disc list-inside">
              <li>1) 先確認需求池：若 <span className="text-emerald-300">{demandSupply.openTaskCount}</span> 筆，請先確認任務文案是否有可執行 step。</li>
              <li>2) 重試建議：再打 <span className="font-mono text-xs">/api/open-tasks</span> 3 次（每次間隔 30~60 秒）確認回傳一致。</li>
              <li>3) 直接驗證任務描述摘要，避免只用含「GitHub Issue:」字頭（現已過濾）。</li>
              <li>4) 若仍未有接單：將任務 reward、required_stack 與 developer_wallet 指引補齊為固定欄位範例。</li>
            </ul>
          </div>
        </div>

        <div className="grid gap-4 lg:grid-cols-2">
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">外部流量轉化漏斗</h2>
            <div className="space-y-2 text-sm">
              <div className="flex justify-between">
                <span>A2A曝光→提案頁</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.proposal_view_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-sky-400"
                  style={{ width: `${Math.min(conversionRates.proposal_view_rate, 100)}%` }}
                />
              </div>
              <div className="flex justify-between">
                <span>提案頁→建立</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.proposal_create_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-violet-400"
                  style={{ width: `${Math.min(conversionRates.proposal_create_rate, 100)}%` }}
                />
              </div>
              <div className="flex justify-between">
                <span>建立→付款成功</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.proposal_paid_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-emerald-400"
                  style={{ width: `${Math.min(conversionRates.proposal_paid_rate, 100)}%` }}
                />
              </div>
              <div className="flex justify-between">
                <span>曝光→接案</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.claim_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-emerald-400"
                  style={{ width: `${Math.min(conversionRates.claim_rate, 100)}%` }}
                />
              </div>
              <div className="flex justify-between">
                <span>接案→提交</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.submit_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-cyan-400"
                  style={{ width: `${Math.min(conversionRates.submit_rate, 100)}%` }}
                />
              </div>
              <div className="flex justify-between">
                <span>提交→PASS</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.pass_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-blue-400"
                  style={{ width: `${Math.min(conversionRates.pass_rate, 100)}%` }}
                />
              </div>
              <div className="flex justify-between">
                <span>PASS→收款成功</span>
                <span className="text-emerald-300">{fmtPercent(conversionRates.payout_rate)}</span>
              </div>
              <div className="h-2 bg-gray-800 rounded-full overflow-hidden">
                <div
                  className="h-full bg-amber-400"
                  style={{ width: `${Math.min(conversionRates.payout_rate, 100)}%` }}
                />
              </div>
            </div>
            <div className="mt-4 text-sm text-gray-300 space-y-1">
              <div className="flex justify-between"><span>Stripe checkout 開始</span><span>{conversionSummary.proposal_checkout_events}</span></div>
              <div className="flex justify-between"><span>錢包付款待確認</span><span>{conversionSummary.proposal_wallet_pending_events}</span></div>
              <div className="flex justify-between"><span>PASS 任務</span><span>{conversionSummary.judge_pass_events}</span></div>
              <div className="flex justify-between"><span>FAIL 任務</span><span>{conversionSummary.judge_fail_events}</span></div>
              <div className="flex justify-between"><span>已出金</span><span>{conversionSummary.payout_captured}</span></div>
              <div className="flex justify-between"><span>已退費</span><span>{conversionSummary.payout_released}</span></div>
            </div>
          </div>

          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">轉化流程與使用者後續處理（你現在該做什麼）</h2>
            <ol className="text-sm text-gray-200 space-y-2 list-decimal list-inside">
              <li>曝光端流量先判斷：從 open-tasks / MCP 的呼叫量，看是否有可感知的外部探索流。</li>
              <li>接案事件代表 AI 真的把任務當作可接單：EXTERNAL_CLAIM_TASK_SUCCESS。</li>
              <li>提交事件代表 AI 真的開始交件：EXTERNAL_SUBMIT_SOLUTION_SUCCESS。</li>
              <li>系統 PASS 才有收款機會：JUDGE_COMPLETE + overall_result=PASS，接著做 CAPTURE。</li>
              <li>你要追的是「曝光→接案→提交→PASS→收款」連續率，而不是單一事件數量。</li>
            </ol>
            {hasClaimStall && demandSupply.openTaskCount > 0 ? (
              <div className="mt-4 p-3 border border-amber-500/40 rounded-lg text-sm text-amber-200">
                本輪有 EXTERNAL_FUNNEL_CLAIM_STALL 告警。可直接採取：
                <ul className="list-disc list-inside mt-2 space-y-1">
                  <li>先取一筆最新 OPEN 任務，核對 `scope_clarity_score` 與 reward 欄位。</li>
                  <li>發送 1 次固定模板 `claim_task` 試探，若失敗 403 則先修正 API Key/權限。</li>
                  <li>若返回 200 仍無接案，調整任務文案再行發佈，讓外部判斷標準更明確。</li>
                </ul>
              </div>
            ) : null}
            <div className="mt-4 text-sm text-gray-400">建議每 30 分鐘查看一次此漏斗，針對斷崖段落補任務內容或加標籤。</div>
          </div>
        </div>

        <div className="grid gap-4 lg:grid-cols-2">
          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">外部流量來源分類（近 120 筆事件）</h2>
            <div className="space-y-2">
              {summary.externalActorClassSummary.length === 0 ? (
                <p className="text-gray-500">目前區間內尚無外部分類資料。</p>
              ) : (
                summary.externalActorClassSummary.map((item) => (
                  <div key={item.actor_class} className="flex justify-between text-sm">
                    <span className="text-gray-300">{actorClassLabel(item.actor_class)}</span>
                    <span className="text-emerald-300">{item.events}</span>
                  </div>
                ))
              )}
            </div>
          </div>

          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">外部來源 Actor 前 20</h2>
            <div className="space-y-2 max-h-80 overflow-auto">
              {summary.externalActorSummary.length === 0 ? (
                <p className="text-gray-500">目前區間內尚無外部 Actor。</p>
              ) : (
                summary.externalActorSummary.map((actor) => (
                  <div key={actor.actorId} className="flex justify-between text-sm">
                    <span className="text-gray-300">{actor.actorId}</span>
                    <span className="text-emerald-300">{actor.events}</span>
                  </div>
                ))
              )}
            </div>
          </div>

          <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
            <h2 className="text-xl font-semibold mb-4">Actor 類型分布</h2>
            <div className="space-y-2">
              {Object.entries(summary.actorSummary).map(([actorType, count]) => (
                <div key={actorType} className="flex justify-between text-sm">
                  <span className="text-gray-300">{actorType}</span>
                  <span className="text-blue-300">{count}</span>
                </div>
              ))}
              {Object.keys(summary.actorSummary).length === 0 ? (
                <p className="text-gray-500 text-sm">暫無資料</p>
              ) : null}
            </div>
          </div>
        </div>

        <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
          <h2 className="text-xl font-semibold mb-4">外部 Actor 行為追蹤（可追溯）</h2>
          <div className="overflow-auto max-h-96">
            <table className="w-full text-sm">
              <thead>
                <tr className="text-gray-400 border-b border-gray-700">
                  <th className="text-left py-2">來源類型</th>
                  <th className="text-left py-2">Actor</th>
                  <th className="text-left py-2">事件</th>
                  <th className="text-left py-2">最新行為</th>
                  <th className="text-left py-2">任務</th>
                  <th className="text-left py-2">來源 IP</th>
                  <th className="text-left py-2">User-Agent</th>
                  <th className="text-left py-2">Request-Id</th>
                  <th className="text-left py-2">最新回應</th>
                </tr>
              </thead>
              <tbody>
                {summary.externalActorActivities.length === 0 ? (
                  <tr>
                    <td colSpan={9} className="text-gray-500 py-3">
                      尚無可追蹤的外部 AGENT 行為，可能目前仍未有 AGENT 類型入口流量。
                    </td>
                  </tr>
                ) : (
                  summary.externalActorActivities.map((actor) => (
                    <tr key={actor.actorId} className="border-b border-gray-800">
                      <td className="py-2 text-gray-300">{actorClassLabel(actor.actorClass)}</td>
                      <td className="py-2 text-gray-300">{actor.actorId}</td>
                      <td className="py-2 text-emerald-300">{actor.events}</td>
                      <td className="py-2">
                        <div className="text-gray-200">{actor.latestAction}</div>
                        <div className="text-xs text-gray-500">{actor.latestSurface}</div>
                      </td>
                      <td className="py-2 text-gray-300">{actor.latestTaskId}</td>
                      <td className="py-2 text-gray-300">{actor.latestSourceIp}</td>
                      <td className="py-2 text-gray-300">{actor.latestUserAgent}</td>
                      <td className="py-2 text-gray-300">{actor.latestRequestId}</td>
                      <td className="py-2">
                        <div className="text-gray-200">{actor.latestResponseStatus ?? "n/a"}</div>
                        <div className="text-xs text-gray-500">{actor.latestResponseSummary}</div>
                        <div className="text-xs text-gray-600">{actor.latestPayloadSummary}</div>
                      </td>
                    </tr>
                  ))
                )}
              </tbody>
            </table>
          </div>
        </div>

        <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
          <h2 className="text-xl font-semibold mb-4">外部事件說明（可讀）</h2>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-2 mb-4 text-sm">
            {Object.entries(summary.actionSummary)
              .filter(([action]) => action.startsWith("EXTERNAL_"))
              .sort((a, b) => b[1] - a[1])
              .slice(0, 14)
              .map(([action, count]) => (
                <div key={action} className="flex justify-between border-b border-gray-800 py-1">
                  <span className="text-gray-300">{explainAction(action)} ({action})</span>
                  <span className="text-emerald-300">{count}</span>
                </div>
              ))}
          </div>
          <div className="text-sm text-gray-300 space-y-1">
            <p>事件方向：</p>
            {Object.entries(summary.actionSummary)
              .filter(([action]) => action.startsWith("EXTERNAL_"))
              .map(([action, count]) => (
                <div key={action} className="flex justify-between">
                  <span className="text-gray-400">{eventDirection(action)}{action}</span>
                  <span>{count}</span>
                </div>
              ))}
          </div>
        </div>

        <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
          <h2 className="text-xl font-semibold mb-4">可執行轉化建議</h2>
          <ul className="space-y-2 text-sm text-gray-200 list-disc list-inside">
            {conversionHints.map((item, index) => (
              <li key={`hint-${index}`}>{item}</li>
            ))}
          </ul>
          {summary.externalErrors.length > 0 ? (
            <div className="mt-4 text-sm text-amber-300">
              注意：目前已偵測到 {summary.externalErrors.length} 種外部錯誤事件（{summary.externalErrors.join(", ")}），請優先回查 API payload 與 auth policy。
            </div>
          ) : null}
        </div>

        <div className="bg-gray-900 border border-gray-800 rounded-2xl p-6">
          <h2 className="text-xl font-semibold mb-4">最近外部事件（top 120）</h2>
          <div className="space-y-2 max-h-96 overflow-auto">
            {summary.recentExternalEvents.length === 0 ? (
              <p className="text-gray-500">目前區間內尚無外部事件。</p>
            ) : (
              summary.recentExternalEvents.map((event) => {
                const ts = toLocalTime(event.createdAt);
                return (
                  <div key={event.id} className="border-b border-gray-800 py-2 text-sm">
                    <div className="font-mono text-emerald-300">{event.action}</div>
                    <div className="text-gray-400">
                      actor={event.actorType}:{event.actorId || "unknown"} | 類別={actorClassLabel((event as { actor_class?: TrafficActorClass }).actor_class || "other_external")} | entity={event.entityType}/{event.entityId} | surface={String(event.surface || "-")} | {ts}
                    </div>
                    <div className="text-gray-500 text-xs mt-1">
                      response={event.response_status ?? "n/a"} / summary={event.response_summary}
                    </div>
                    {event.reason ? <div className="text-gray-500 text-xs mt-1">{event.reason}</div> : null}
                    {event.metadata ? (
                      <pre className="text-xs text-gray-500 mt-1 whitespace-pre-wrap">
                        {JSON.stringify(event.metadata, null, 2)}
                      </pre>
                    ) : null}
                  </div>
                );
              })
            )}
          </div>
        </div>
      </div>
    </div>
  );
}
