import type { CrossoverPair, AttributeDAG, CategoryDefinition, DAGNode } from '../types';

/**
 * Result of crossover transformation - two separate DAGs
 * Each path receives attributes from the other path
 */
export interface CrossoverDAGResult {
  // Path A receives attributes from Path B
  pathA: AttributeDAG;
  // Path B receives attributes from Path A
  pathB: AttributeDAG;
}

/**
 * Convert selected crossover pairs into two separate AttributeDAGs.
 *
 * The crossover logic:
 * - When pair (A's "防水布" × B's "鋁合金") is selected:
 *   - Path A gets "鋁合金" (from B) to transform with A's context
 *   - Path B gets "防水布" (from A) to transform with B's context
 *
 * This allows experts to think about:
 * - "What if umbrella had aluminum alloy?" (A gets B's attribute)
 * - "What if bicycle had waterproof fabric?" (B gets A's attribute)
 */
export function crossoverPairsToDAGs(
  pairs: CrossoverPair[],
  dagA: AttributeDAG,
  dagB: AttributeDAG
): CrossoverDAGResult {
  // Collect attributes that each path receives from the other
  const attributesForA: Map<string, Set<string>> = new Map(); // category -> attributes from B
  const attributesForB: Map<string, Set<string>> = new Map(); // category -> attributes from A

  for (const pair of pairs) {
    // Path A receives the target node (from B)
    // Path B receives the source node (from A)
    if (pair.sourcePathId === 'A' && pair.targetPathId === 'B') {
      // A's attribute crossed with B's attribute
      // A gets B's attribute, B gets A's attribute
      const categoryForA = pair.targetNode.category;
      const categoryForB = pair.sourceNode.category;

      if (!attributesForA.has(categoryForA)) attributesForA.set(categoryForA, new Set());
      if (!attributesForB.has(categoryForB)) attributesForB.set(categoryForB, new Set());

      attributesForA.get(categoryForA)!.add(pair.targetNode.name);
      attributesForB.get(categoryForB)!.add(pair.sourceNode.name);
    } else if (pair.sourcePathId === 'B' && pair.targetPathId === 'A') {
      // B's attribute crossed with A's attribute
      // A gets B's attribute, B gets A's attribute (reverse direction)
      const categoryForA = pair.sourceNode.category;
      const categoryForB = pair.targetNode.category;

      if (!attributesForA.has(categoryForA)) attributesForA.set(categoryForA, new Set());
      if (!attributesForB.has(categoryForB)) attributesForB.set(categoryForB, new Set());

      attributesForA.get(categoryForA)!.add(pair.sourceNode.name);
      attributesForB.get(categoryForB)!.add(pair.targetNode.name);
    }
  }

  // Build DAG for Path A (receives attributes from B)
  const pathAResult = buildCrossoverDAG(
    dagA.query,
    attributesForA,
    `Crossover: ${dagB.query} → ${dagA.query}`
  );

  // Build DAG for Path B (receives attributes from A)
  const pathBResult = buildCrossoverDAG(
    dagB.query,
    attributesForB,
    `Crossover: ${dagA.query} → ${dagB.query}`
  );

  return {
    pathA: pathAResult,
    pathB: pathBResult,
  };
}

/**
 * Build an AttributeDAG from crossover attributes
 */
function buildCrossoverDAG(
  originalQuery: string,
  attributesByCategory: Map<string, Set<string>>,
  _crossoverDescription: string
): AttributeDAG {
  const categories: CategoryDefinition[] = [];
  const nodes: DAGNode[] = [];
  let nodeIndex = 0;
  let categoryIndex = 0;

  for (const [categoryName, attributes] of attributesByCategory.entries()) {
    categories.push({
      name: categoryName,
      description: `Crossover attributes for ${categoryName}`,
      is_fixed: false,
      order: categoryIndex,
    });
    categoryIndex++;

    for (const attrName of attributes) {
      nodes.push({
        id: `crossover-${nodeIndex}`,
        name: attrName,
        category: categoryName,
        order: nodeIndex,
      });
      nodeIndex++;
    }
  }

  return {
    query: originalQuery,
    categories,
    nodes,
    edges: [],
  };
}

/**
 * Legacy function - converts to single DAG (deprecated)
 * Kept for backwards compatibility
 */
export function crossoverPairsToDAG(
  pairs: CrossoverPair[],
  queryA: string,
  queryB: string
): AttributeDAG {
  // Group pairs by crossType
  const pairsByType = pairs.reduce((acc, pair) => {
    if (!acc[pair.crossType]) acc[pair.crossType] = [];
    acc[pair.crossType].push(pair);
    return acc;
  }, {} as Record<string, CrossoverPair[]>);

  // Create categories from crossTypes
  const categories: CategoryDefinition[] = Object.keys(pairsByType).map((crossType, index) => ({
    name: formatCrossTypeName(crossType),
    description: getCrossTypeDescription(crossType),
    is_fixed: false,
    order: index,
  }));

  // Create nodes from pairs
  const nodes: DAGNode[] = [];
  let nodeIndex = 0;

  for (const [crossType, typePairs] of Object.entries(pairsByType)) {
    const categoryName = formatCrossTypeName(crossType);

    for (const pair of typePairs) {
      nodes.push({
        id: `crossover-${nodeIndex}`,
        name: `${pair.sourceNode.name} × ${pair.targetNode.name}`,
        category: categoryName,
        order: nodeIndex,
      });
      nodeIndex++;
    }
  }

  return {
    query: `${queryA} × ${queryB}`,
    categories,
    nodes,
    edges: [],
  };
}

function formatCrossTypeName(crossType: string): string {
  if (crossType.startsWith('same-')) {
    const category = crossType.replace('same-', '');
    return `Same: ${category}`;
  }
  if (crossType.startsWith('cross-')) {
    const parts = crossType.replace('cross-', '').split('-');
    if (parts.length >= 2) {
      return `Cross: ${parts[0]} × ${parts.slice(1).join('-')}`;
    }
  }
  return crossType;
}

function getCrossTypeDescription(crossType: string): string {
  if (crossType.startsWith('same-')) {
    const category = crossType.replace('same-', '');
    return `Crossover pairs from the same category: ${category}`;
  }
  if (crossType.startsWith('cross-')) {
    return `Crossover pairs from different categories`;
  }
  return `Crossover pairs`;
}
