import dagre from "@dagrejs/dagre";

import type { CharacterCardView } from "@/lib/api/types";

// 关系图布局纯逻辑（灵感⑥ 缩减版）：把「角色 + relations」转成节点/边并跑一次性
// dagre 初始坐标。仅用现有读端点数据（CharacterRelationView={name,kind,note}）——
// 无 closeness/阵营/target-id，节点按 role 上色，边按 name-join、kind 作中文标签。
// 无 DOM 依赖，可在 node 环境单测；React Flow 组件只做渲染，不含此处逻辑。

// 节点尺寸（dagre 布局与 React Flow 渲染共用；命名常量非魔法值）。
export const NODE_WIDTH = 176;
export const NODE_HEIGHT = 56;
// dagre 间距。
export const DAGRE_RANK_SEP = 88;
export const DAGRE_NODE_SEP = 48;
// kind 缺失时的关系标签回退（命名常量非魔法值）。
export const DEFAULT_RELATION_LABEL = "关系";

// 角色定位分组（role 是自由中文文本，无固定枚举 → 关键词映射到有限分组）。
export type RoleGroup =
  | "protagonist"
  | "antagonist"
  | "mentor"
  | "ally"
  | "supporting"
  | "other";

export interface RoleStyle {
  group: RoleGroup;
  // 纸感主题 CSS 变量名（随亮/暗主题自适应，不写死色值）。
  colorVar: string;
}

// role→分组关键词规则；按序匹配，首个命中生效，否则回退 other。
const ROLE_RULES: ReadonlyArray<{
  keywords: readonly string[];
  style: RoleStyle;
}> = [
  {
    keywords: ["主角", "主人公", "男主", "女主"],
    style: { group: "protagonist", colorVar: "--color-cinnabar" },
  },
  {
    keywords: ["反派", "对手", "宿敌", "敌", "boss"],
    style: { group: "antagonist", colorVar: "--color-conflict" },
  },
  {
    keywords: ["导师", "师父", "师傅", "师尊", "老师"],
    style: { group: "mentor", colorVar: "--color-info" },
  },
  {
    keywords: ["cp", "恋", "爱人", "伴侣", "挚友", "知己"],
    style: { group: "ally", colorVar: "--color-pass" },
  },
  {
    keywords: ["配角", "工具人", "路人", "龙套"],
    style: { group: "supporting", colorVar: "--color-ink-soft" },
  },
];

const DEFAULT_ROLE_STYLE: RoleStyle = {
  group: "other",
  colorVar: "--color-ink-soft",
};

// 把角色定位映射到分组/配色（大小写不敏感，子串匹配）。
export function roleStyle(role: string): RoleStyle {
  const normalized = role.trim().toLowerCase();
  if (!normalized) {
    return DEFAULT_ROLE_STYLE;
  }
  for (const rule of ROLE_RULES) {
    if (rule.keywords.some((keyword) => normalized.includes(keyword))) {
      return rule.style;
    }
  }
  return DEFAULT_ROLE_STYLE;
}

export interface RelationNodeData {
  label: string;
  role: string;
  group: RoleGroup;
  colorVar: string;
}

export interface GraphNode {
  id: string; // = 角色名（name-join 主键）
  position: { x: number; y: number };
  data: RelationNodeData;
}

export interface GraphEdge {
  id: string;
  source: string;
  target: string;
  label: string;
}

export interface RelationshipGraphData {
  nodes: GraphNode[];
  edges: GraphEdge[];
  // 引用未入库角色而被略过的关系条数（供 UI 提示）。
  danglingCount: number;
}

// 每个唯一角色一个节点；空名/重名（先到先得）跳过。位置初始 {0,0}，由 layoutGraph 赋值。
export function buildGraphNodes(
  characters: readonly CharacterCardView[],
): GraphNode[] {
  const seen = new Set<string>();
  const nodes: GraphNode[] = [];
  for (const character of characters) {
    const name = character.name.trim();
    if (!name || seen.has(name)) {
      continue;
    }
    seen.add(name);
    const style = roleStyle(character.role);
    nodes.push({
      id: name,
      position: { x: 0, y: 0 },
      data: {
        label: name,
        role: character.role,
        group: style.group,
        colorVar: style.colorVar,
      },
    });
  }
  return nodes;
}

// 无向对键：与方向无关，用于 A↔B 去重。
function pairKey(a: string, b: string): string {
  return [a, b].sort().join("");
}

// 按 name-join 构边：kind→标签；悬空边（目标不在 knownNames）与自指跳过；互指无向去重。
export function buildGraphEdges(
  characters: readonly CharacterCardView[],
  knownNames: ReadonlySet<string>,
): GraphEdge[] {
  const edges: GraphEdge[] = [];
  const seenPairs = new Set<string>();
  let index = 0;
  for (const character of characters) {
    const source = character.name.trim();
    if (!source || !knownNames.has(source)) {
      continue;
    }
    for (const relation of character.relations ?? []) {
      const target = relation.name.trim();
      if (!target || !knownNames.has(target) || target === source) {
        continue;
      }
      const key = pairKey(source, target);
      if (seenPairs.has(key)) {
        continue;
      }
      seenPairs.add(key);
      const label = relation.kind.trim() || DEFAULT_RELATION_LABEL;
      edges.push({
        id: `e-${index}-${source}-${target}`,
        source,
        target,
        label,
      });
      index += 1;
    }
  }
  return edges;
}

// 统计引用未入库角色（悬空）的关系条数——供 UI「N 条关系已略过」提示。
export function countDanglingRelations(
  characters: readonly CharacterCardView[],
  knownNames: ReadonlySet<string>,
): number {
  let count = 0;
  for (const character of characters) {
    const source = character.name.trim();
    if (!source || !knownNames.has(source)) {
      continue;
    }
    for (const relation of character.relations ?? []) {
      const target = relation.name.trim();
      if (target && target !== source && !knownNames.has(target)) {
        count += 1;
      }
    }
  }
  return count;
}

interface LaidOutNode {
  x: number;
  y: number;
}

// 跑一次性 dagre 布局（确定性：同输入同坐标）。dagre 给中心坐标 → 转 React Flow 左上角。
// 不可变：返回新节点数组，不修改入参。
export function layoutGraph(
  nodes: readonly GraphNode[],
  edges: readonly GraphEdge[],
): GraphNode[] {
  const graph = new dagre.graphlib.Graph();
  graph.setGraph({
    rankdir: "LR",
    nodesep: DAGRE_NODE_SEP,
    ranksep: DAGRE_RANK_SEP,
  });
  graph.setDefaultEdgeLabel(() => ({}));
  for (const node of nodes) {
    graph.setNode(node.id, { width: NODE_WIDTH, height: NODE_HEIGHT });
  }
  for (const edge of edges) {
    graph.setEdge(edge.source, edge.target);
  }
  dagre.layout(graph);
  return nodes.map((node) => {
    const laid = graph.node(node.id) as LaidOutNode | undefined;
    const centerX = laid?.x ?? 0;
    const centerY = laid?.y ?? 0;
    return {
      ...node,
      position: {
        x: centerX - NODE_WIDTH / 2,
        y: centerY - NODE_HEIGHT / 2,
      },
    };
  });
}

// 端到端：角色列表 → 布局后的节点 + 有效边 + 悬空计数（组件里 useMemo 缓存）。
export function buildRelationshipGraph(
  characters: readonly CharacterCardView[],
): RelationshipGraphData {
  const nodes = buildGraphNodes(characters);
  const knownNames = new Set(nodes.map((node) => node.id));
  const edges = buildGraphEdges(characters, knownNames);
  const danglingCount = countDanglingRelations(characters, knownNames);
  const laidOut = layoutGraph(nodes, edges);
  return { nodes: laidOut, edges, danglingCount };
}
