import { useMemo } from 'react';
import type { Node, Edge } from '@xyflow/react';
import type { TransformationDAGResult, CategoryDefinition } from '../../types';

interface LayoutConfig {
  isDark: boolean;
  fontSize?: number;
}

const COLOR_PALETTE = [
  { dark: '#177ddc', light: '#1890ff' }, // blue
  { dark: '#854eca', light: '#722ed1' }, // purple
  { dark: '#13a8a8', light: '#13c2c2' }, // cyan
  { dark: '#d87a16', light: '#fa8c16' }, // orange
  { dark: '#49aa19', light: '#52c41a' }, // green
  { dark: '#1677ff', light: '#1890ff' }, // blue
  { dark: '#eb2f96', light: '#f759ab' }, // magenta
  { dark: '#faad14', light: '#ffc53d' }, // gold
];

// Estimate description card height based on text length
function estimateDescriptionHeight(description: string): number {
  const cardWidth = 320; // increased card width
  const padding = 20; // top + bottom padding
  const headerHeight = 30; // header section with keyword
  const charPerLine = Math.floor(cardWidth / 13); // ~13px per Chinese char
  const lineHeight = 18; // line height in px
  const lines = Math.ceil(description.length / charPerLine);
  // Cap at reasonable height to prevent huge gaps
  return Math.min(padding + headerHeight + lines * lineHeight, 120);
}

export function useTransformationLayout(
  data: TransformationDAGResult | null,
  categories: CategoryDefinition[],
  config: LayoutConfig
): { nodes: Node[]; edges: Edge[] } {
  return useMemo(() => {
    if (!data || data.results.length === 0) {
      return { nodes: [], edges: [] };
    }

    const { isDark, fontSize = 13 } = config;
    const nodes: Node[] = [];
    const edges: Edge[] = [];

    // Layout constants
    const colStep = 140;
    const categoryRowGap = 160; // increased gap between different categories
    const minItemGap = 12; // minimum gap between transformation items
    const origAttrRowStep = 36; // step for original attributes (same visual rhythm)

    const queryX = 0;
    const categoryX = colStep;
    const originalAttrX = colStep * 2;
    const keywordX = colStep * 3.2; // increased gap from original attributes
    const descriptionX = colStep * 4.8; // moved further right for wider cards

    // Build category color map
    const categoryColors: Record<string, string> = {};
    categories.forEach((cat, index) => {
      const paletteIndex = index % COLOR_PALETTE.length;
      categoryColors[cat.name] = isDark
        ? COLOR_PALETTE[paletteIndex].dark
        : COLOR_PALETTE[paletteIndex].light;
    });

    // Pre-calculate all description heights and keyword Y positions for each category
    interface CategoryLayout {
      keywordYPositions: number[];
      origAttrYPositions: number[];
      totalHeight: number;
    }

    const categoryLayouts: CategoryLayout[] = data.results.map((result) => {
      // Calculate keyword/description Y positions based on description heights
      const keywordYPositions: number[] = [];
      let currentKeywordY = 0;

      result.new_keywords.forEach((keyword) => {
        keywordYPositions.push(currentKeywordY);

        // Find matching description to calculate height
        const matchingDesc = result.descriptions.find((d) => d.keyword === keyword);
        const descHeight = matchingDesc
          ? estimateDescriptionHeight(matchingDesc.description)
          : 50;

        // Next keyword starts after this description
        currentKeywordY += descHeight + minItemGap;
      });

      const keywordTotalHeight = currentKeywordY > 0 ? currentKeywordY - minItemGap : 0;

      // Calculate original attribute positions to match the same total height
      const origCount = result.original_attributes.length;
      const origAttrYPositions: number[] = [];

      if (origCount > 0) {
        // Distribute original attributes evenly across the same height as keywords
        const effectiveHeight = Math.max(keywordTotalHeight, origCount * origAttrRowStep);
        const origStep = origCount > 1 ? effectiveHeight / (origCount - 1) : 0;

        for (let i = 0; i < origCount; i++) {
          origAttrYPositions.push(i * (origCount > 1 ? origStep : 0));
        }
      }

      const totalHeight = Math.max(keywordTotalHeight, origCount * origAttrRowStep);

      return { keywordYPositions, origAttrYPositions, totalHeight };
    });

    // Calculate total height for query centering
    const totalHeight = categoryLayouts.reduce(
      (sum, layout, i) =>
        sum + layout.totalHeight + (i < categoryLayouts.length - 1 ? categoryRowGap : 0),
      0
    );

    // Add Query node (centered vertically)
    const queryY = totalHeight / 2 - 20;
    nodes.push({
      id: 'query-node',
      type: 'query',
      position: { x: queryX, y: queryY },
      data: {
        label: data.query,
        isDark,
        fontSize: fontSize + 2,
      },
      draggable: false,
      selectable: false,
      zIndex: 10,
    });

    // Track current Y position
    let currentY = 0;

    // Process each category result
    data.results.forEach((result, catIndex) => {
      const categoryId = `category-${catIndex}`;
      const color = categoryColors[result.category] || '#666';
      const layout = categoryLayouts[catIndex];

      // Category Y position (centered within its group)
      const categoryY = currentY + layout.totalHeight / 2 - 20;

      // Add Group Node (Background)
      const groupPaddingX = 60;
      const groupPaddingY = 40;
      const groupHeight = layout.totalHeight + (groupPaddingY * 2);
      const groupWidth = descriptionX + 450; // Cover everything up to description end

      nodes.push({
        id: `group-${catIndex}`,
        type: 'group',
        position: {
          x: categoryX - groupPaddingX,
          y: currentY - groupPaddingY
        },
        data: {
          label: result.category, // Add label to group
          color: color,
          width: groupWidth + groupPaddingX,
          height: groupHeight,
          isDark,
        },
        draggable: false,
        selectable: false,
        zIndex: -1,
      });

      // Add category node
      nodes.push({
        id: categoryId,
        type: 'category',
        position: { x: categoryX, y: categoryY },
        data: {
          label: result.category,
          color,
          attributeCount: result.original_attributes.length,
          isDark,
        },
        draggable: false,
        selectable: false,
        zIndex: 5,
      });

      // Edge from query to category
      edges.push({
        id: `edge-query-${categoryId}`,
        source: 'query-node',
        target: categoryId,
        type: 'smoothstep',
        style: { stroke: color, strokeWidth: 2, opacity: 0.6 },
        animated: false,
        label: 'Classified as',
        labelStyle: { fill: isDark ? '#aaa' : '#666', fontSize: 10 },
        labelBgStyle: { fill: isDark ? '#1f1f1f' : '#fff', fillOpacity: 0.8 },
      });

      // Add original attribute nodes (distributed to match keyword spacing)
      result.original_attributes.forEach((attr, attrIndex) => {
        const attrId = `orig-${catIndex}-${attrIndex}`;
        const attrY = currentY + (layout.origAttrYPositions[attrIndex] || 0);

        nodes.push({
          id: attrId,
          type: 'originalAttribute',
          position: { x: originalAttrX, y: attrY },
          data: {
            label: attr,
            color,
            isDark,
          },
          draggable: false,
          selectable: false,
          zIndex: 5,
        });

        // Edge from category to original attribute
        edges.push({
          id: `edge-${categoryId}-${attrId}`,
          source: categoryId,
          target: attrId,
          type: 'smoothstep',
          style: { stroke: color, strokeWidth: 1, opacity: 0.3 },
          animated: false,
        });
      });

      // Add keyword and description nodes with smart Y positions
      result.new_keywords.forEach((keyword, kwIndex) => {
        const keywordId = `keyword-${catIndex}-${kwIndex}`;
        const keywordY = currentY + layout.keywordYPositions[kwIndex];

        // Keyword node
        nodes.push({
          id: keywordId,
          type: 'keyword',
          position: { x: keywordX, y: keywordY },
          data: {
            label: keyword,
            color,
            isDark,
          },
          draggable: false,
          selectable: false,
          zIndex: 5,
        });

        // Edge from category to keyword
        edges.push({
          id: `edge-${categoryId}-${keywordId}`,
          source: categoryId,
          target: keywordId,
          type: 'smoothstep',
          style: {
            stroke: '#faad14',
            strokeWidth: 2,
            opacity: 0.7,
            strokeDasharray: '5,5',
          },
          animated: true,
          label: 'Generates',
          labelStyle: { fill: '#faad14', fontSize: 10, fontWeight: 600 },
          labelBgStyle: { fill: isDark ? '#1f1f1f' : '#fff', fillOpacity: 0.8 },
        });

        // Find matching description
        const matchingDesc = result.descriptions.find((d) => d.keyword === keyword);
        if (matchingDesc) {
          const descId = `desc-${catIndex}-${kwIndex}`;

          nodes.push({
            id: descId,
            type: 'description',
            position: { x: descriptionX, y: keywordY },
            data: {
              keyword: matchingDesc.keyword,
              description: matchingDesc.description,
              color,
              isDark,
            },
            draggable: false,
            selectable: false,
            zIndex: 5,
          });

          // Edge from keyword to description
          edges.push({
            id: `edge-${keywordId}-${descId}`,
            source: keywordId,
            target: descId,
            type: 'smoothstep',
            style: { stroke: '#52c41a', strokeWidth: 2, opacity: 0.6 },
            animated: false,
            label: 'Describes',
            labelStyle: { fill: '#52c41a', fontSize: 10, fontWeight: 600 },
            labelBgStyle: { fill: isDark ? '#1f1f1f' : '#fff', fillOpacity: 0.8 },
          });
        }
      });

      // Move Y position for next category
      currentY += layout.totalHeight;

      // Add extra gap between categories
      currentY += categoryRowGap;
    });

    return { nodes, edges };
  }, [data, categories, config]);
}
