import type { BrandData, BrandProductData, KnowledgeGraphData, KnowledgeGraphNodeData, PlacementTopicData } from "../api/haixun";

export type PatrolKeywordOrigin =
  | "manual"
  | "selected_node"
  | "selected_tag"
  | "research_map"
  | "research_question"
  | "graph_pain"
  | "product"
  | "product_tag"
  | "seed";

export type PatrolKeywordItem = {
  keyword: string;
  origin: PatrolKeywordOrigin;
  detail: string;
};

export type PatrolSourceGroup = {
  key: string;
  title: string;
  hint: string;
  items: PatrolKeywordItem[];
};

export type PatrolPlan = {
  keywords: string[];
  finalItems: PatrolKeywordItem[];
  groups: PatrolSourceGroup[];
  selectedNodeCount: number;
  productLabel: string;
  productMatchTags: string[];
  queryBudget: number;
  isManual: boolean;
};

const ORIGIN_LABELS: Record<PatrolKeywordOrigin, string> = {
  manual: "手動覆寫",
  selected_node: "勾選節點",
  selected_tag: "勾選節點標籤",
  research_map: "研究地圖",
  research_question: "受眾痛點",
  graph_pain: "圖譜痛點",
  product: "推薦產品",
  product_tag: "產品標籤",
  seed: "種子關鍵字",
};

export function patrolOriginLabel(origin: PatrolKeywordOrigin) {
  return ORIGIN_LABELS[origin];
}

function painishNode(node: KnowledgeGraphNodeData) {
  const tags = (node.relevance_tags || []).join(" ");
  return (
    node.node_kind === "pain" ||
    /痛|問題|困擾|怎麼辦|推薦/.test(tags) ||
    (node.product_fit_score ?? 0) >= 55
  );
}

function keywordDedupeKey(keyword: string) {
  return keyword.trim().replace(/\s*(推薦|請問|怎麼辦|好用嗎|有人用過嗎|有推薦嗎|請益)\s*$/u, "").trim();
}

function scorePatrolKeyword(
  item: PatrolKeywordItem,
  product: BrandProductData | null,
  selectedNodes: KnowledgeGraphNodeData[]
) {
  const kw = item.keyword.trim().toLowerCase();
  if (!kw) return 0;
  let score = 0;
  switch (item.origin) {
    case "manual":
      score += 120;
      break;
    case "selected_tag":
      score += 86;
      break;
    case "selected_node":
      score += 78;
      break;
    case "research_map":
      score += 68;
      break;
    case "research_question":
      score += 74;
      break;
    case "product_tag":
      score += 64;
      break;
    case "product":
      score += 58;
      break;
    case "graph_pain":
      score += 52;
      break;
    case "seed":
      score += 40;
      break;
    default:
      score += 30;
  }
  const productBlob = `${product?.label || ""} ${(product?.match_tags || []).join(" ")}`.toLowerCase();
  for (const tag of product?.match_tags || []) {
    const t = tag.toLowerCase();
    if (t && (kw.includes(t) || t.includes(kw))) score += 22;
  }
  if (product?.label && kw.includes(product.label.toLowerCase())) score += 18;
  if (/敏感|無香|抗敏|化療|癌症|乳癌|過敏|香精|香味/.test(productBlob) && /敏感|無香|抗敏|化療|癌症|乳癌|過敏|香精|香味/.test(kw)) {
    score += 20;
  }
  if (/洗衣精|洗衣粉|沐浴乳|洗髮精/.test(productBlob) && /洗衣機|烘衣機|洗脫烘/.test(kw) && !/洗衣精|洗衣粉|沐浴乳|洗髮精|無香|抗敏/.test(kw)) {
    score -= 40;
  }
  const node = selectedNodes.find((n) => n.label === item.keyword || (n.relevance_tags || []).includes(item.keyword));
  if (node?.product_fit_score) score += Math.min(24, Math.round(node.product_fit_score / 4));
  return score;
}

function pickFinalKeywords(
  items: PatrolKeywordItem[],
  limit: number,
  product: BrandProductData | null,
  selectedNodes: KnowledgeGraphNodeData[]
): PatrolKeywordItem[] {
  const seen = new Set<string>();
  const ranked = items
    .map((item) => ({
      item: { ...item, keyword: item.keyword.trim() },
      score: scorePatrolKeyword(item, product, selectedNodes),
      key: keywordDedupeKey(item.keyword),
    }))
    .filter((row) => row.item.keyword && row.score > 0)
    .sort((a, b) => b.score - a.score || a.item.keyword.localeCompare(b.item.keyword));

  const out: PatrolKeywordItem[] = [];
  for (const row of ranked) {
    if (seen.has(row.key)) continue;
    seen.add(row.key);
    out.push(row.item);
    if (out.length >= limit) break;
  }
  return out;
}

export function resolvePatrolProduct(brand?: BrandData | null, topic?: PlacementTopicData | null): BrandProductData | null {
  if (!brand?.products?.length) return null;
  const productId = topic?.product_id || brand.product_id;
  if (!productId) return brand.products[0] || null;
  return brand.products.find((p) => p.id === productId) || brand.products[0] || null;
}

const CATEGORY_TOKENS = ["洗衣精", "洗衣粉", "洗衣劑", "洗衣液", "沐浴乳", "沐浴露", "洗髮精", "洗髮乳", "洗碗精", "洗碗劑"] as const;

function categoryTokens(product: BrandProductData) {
  const blob = `${product.label || ""} ${(product.match_tags || []).join(" ")}`.toLowerCase();
  return CATEGORY_TOKENS.filter((token) => blob.includes(token.toLowerCase()));
}

export function scoreProductForTag(searchTag: string, matchTags: string[]) {
  const tag = searchTag.trim().toLowerCase();
  if (!tag || matchTags.length === 0) return 0;
  let best = 0;
  for (const raw of matchTags) {
    const candidate = raw.trim().toLowerCase();
    if (!candidate) continue;
    if (tag === candidate) best = Math.max(best, 100);
    else if (tag.includes(candidate) || candidate.includes(tag)) best = Math.max(best, 70);
    else {
      const tagWords = tag.split(/\s+/);
      const candWords = candidate.split(/\s+/);
      let overlap = 0;
      for (const w of tagWords) {
        if (candWords.some((c) => c.includes(w) || w.includes(c))) overlap += 1;
      }
      if (overlap > 0) best = Math.max(best, 40 + overlap * 10);
    }
  }
  return best;
}

export function scoreProductForPost(product: BrandProductData, searchTag: string, postText: string) {
  const tags = [...(product.match_tags || [])];
  if (product.label) tags.push(product.label);

  let tagScore = scoreProductForTag(searchTag, tags);
  let postScore = scoreProductForTag(postText, tags);

  for (const token of categoryTokens(product)) {
    const lower = token.toLowerCase();
    if (postText.toLowerCase().includes(lower)) postScore = Math.max(postScore, 78);
    if (searchTag.toLowerCase().includes(lower)) tagScore = Math.max(tagScore, 82);
  }

  if (tagScore > 0 && postScore > 0) return Math.round((tagScore * 2 + postScore * 3) / 5);
  if (postScore > 0) return postScore;
  return tagScore;
}

export function resolveProductForPost(input: {
  brand?: BrandData | null;
  topic?: PlacementTopicData | null;
  searchTag?: string;
  postText?: string;
}): BrandProductData | null {
  const products = input.brand?.products || [];
  if (products.length === 0) return null;

  const searchTag = (input.searchTag || "").trim();
  const postText = (input.postText || "").trim();
  const preferredId = input.topic?.product_id || input.brand?.product_id || "";

  let best: BrandProductData | null = null;
  let bestScore = 0;
  for (const product of products) {
    const score = scoreProductForPost(product, searchTag, postText);
    if (score > bestScore) {
      bestScore = score;
      best = product;
    }
  }
  if (best && bestScore >= 55) return best;

  if (preferredId) {
    const preferred = products.find((p) => p.id === preferredId);
    if (preferred) return preferred;
  }
  if (searchTag) {
    for (const product of products) {
      if (scoreProductForTag(searchTag, product.match_tags || []) > 0) return product;
    }
  }
  return products[0] || null;
}

export function buildPatrolPlan(input: {
  topic?: PlacementTopicData | null;
  graph?: KnowledgeGraphData | null;
  brand?: BrandData | null;
  manualKeywords?: string;
  maxKeywords?: number;
}): PatrolPlan {
  const maxKeywords = input.maxKeywords ?? 10;
  const product = resolvePatrolProduct(input.brand, input.topic);
  const selectedNodes = (input.graph?.nodes || []).filter((n) => n.selected_for_scan);
  const manual = (input.manualKeywords || "").trim();
  const groups: PatrolSourceGroup[] = [];

  const manualItems: PatrolKeywordItem[] = manual
    ? manual.split(/\s+/).filter(Boolean).map((keyword) => ({
        keyword,
        origin: "manual" as const,
        detail: "手動提示（優先候選，仍會與其他來源比對相關性）",
      }))
    : [];

  const selectedGroup: PatrolSourceGroup = {
    key: "selected",
    title: "① 你勾選的知識圖譜節點",
    hint: "只有打勾的節點會進來，沒勾選的不會混入",
    items: [],
  };
  if (selectedNodes.length > 0) {
    const sorted = [...selectedNodes].sort((a, b) => b.product_fit_score - a.product_fit_score);
    for (const node of sorted) {
      const nodeName = node.label || node.id;
      if (node.label) {
        selectedGroup.items.push({
          keyword: node.label,
          origin: "selected_node",
          detail: `節點「${nodeName}」· 契合 ${node.product_fit_score}`,
        });
      }
      for (const tag of (node.relevance_tags || []).slice(0, 4)) {
        selectedGroup.items.push({
          keyword: tag,
          origin: "selected_tag",
          detail: `節點「${nodeName}」的搜尋標籤`,
        });
      }
      for (const tag of (node.recency_tags || []).slice(0, 2)) {
        selectedGroup.items.push({
          keyword: tag,
          origin: "selected_tag",
          detail: `節點「${nodeName}」的近期情境標籤`,
        });
      }
    }
  }
  groups.push(selectedGroup);

  const mapGroup: PatrolSourceGroup = {
    key: "research_map",
    title: "② 研究地圖候選",
    hint: "僅供參考；實際送出會依產品意圖挑最相關的搜尋句",
    items: [],
  };
  for (const kw of input.topic?.research_map?.patrol_keywords || []) {
    mapGroup.items.push({
      keyword: kw,
      origin: "research_map",
      detail: "研究地圖 · 建議搜尋關鍵字",
    });
  }
  for (const q of (input.topic?.research_map?.questions || []).slice(0, 6)) {
    mapGroup.items.push({
      keyword: q,
      origin: "research_question",
      detail: "研究地圖 · 受眾痛點問題",
    });
  }
  groups.push(mapGroup);

  const productGroup: PatrolSourceGroup = {
    key: "product",
    title: "③ 推薦產品與匹配標籤",
    hint: "從此主題綁定的產品名稱與 match tags 補強搜尋",
    items: [],
  };
  if (product?.match_tags?.length) {
    for (const tag of product.match_tags) {
      productGroup.items.push({
        keyword: tag,
        origin: "product_tag",
        detail: `產品「${product.label}」匹配標籤`,
      });
    }
  }
  if (product?.label) {
    productGroup.items.push({
      keyword: product.label,
      origin: "product",
      detail: `產品名稱「${product.label}」`,
    });
  }
  groups.push(productGroup);

  if (selectedNodes.length === 0 && input.graph?.nodes?.length) {
    const painGroup: PatrolSourceGroup = {
      key: "graph_fallback",
      title: "（備援）圖譜痛點節點",
      hint: "你尚未勾選節點時，系統才會參考這些高契合痛點",
      items: [],
    };
    const painNodes = input.graph.nodes.filter(painishNode).sort((a, b) => b.product_fit_score - a.product_fit_score);
    for (const node of painNodes.slice(0, 4)) {
      if (!node.label) continue;
      painGroup.items.push({
        keyword: node.label,
        origin: "graph_pain",
        detail: `未勾選 · 痛點候選「${node.label}」`,
      });
    }
    if (painGroup.items.length > 0) groups.push(painGroup);
  }

  const mergeOrder: PatrolKeywordItem[] = [...manualItems];
  const pushAll = (items: PatrolKeywordItem[]) => {
    for (const item of items) mergeOrder.push(item);
  };

  pushAll(selectedGroup.items);
  pushAll(mapGroup.items);
  pushAll(productGroup.items);
  const painFallback = groups.find((g) => g.key === "graph_fallback");
  if (painFallback) pushAll(painFallback.items);

  if (input.topic?.seed_query?.trim()) {
    mergeOrder.push({
      keyword: input.topic.seed_query.trim(),
      origin: "seed",
      detail: "主題種子關鍵字",
    });
  }

  if (manualItems.length > 0) {
    groups.unshift({
      key: "manual",
      title: "⓪ 手動提示",
      hint: "你輸入的詞會加權，但不保證原樣送出；系統會挑最相關的送去 Search API",
      items: manualItems,
    });
  }

  const finalItems = pickFinalKeywords(mergeOrder, maxKeywords, product, selectedNodes);

  return {
    keywords: finalItems.map((i) => i.keyword),
    finalItems,
    groups,
    selectedNodeCount: selectedNodes.length,
    productLabel: product?.label || "未指定產品",
    productMatchTags: product?.match_tags || [],
    queryBudget: Math.min(Math.max(finalItems.length, 1) * 2, 20),
    isManual: manualItems.length > 0,
  };
}