"use client";

import React, { useState, useMemo, useCallback } from "react";
import { createPortal } from "react-dom";
import Link from "next/link";
import Tree from 'react-d3-tree';
import { ArrowsPointingOutIcon, ArrowsPointingInIcon } from "@heroicons/react/24/outline";
import PathDisplay from "./PathDisplay";
import { useMeshcoreSearches } from "@/hooks/useMeshcoreSearch";
import type { MeshcoreSearchResult } from "@/hooks/useMeshcoreSearch";
import { useConfigWithRegion } from "@/hooks/useConfigWithRegion";

export interface PathData {
  origin: string;
  pubkey: string;
  path: string;
}

interface PathGroup {
  path: string;
  pathSlices: string[];
  indices: number[];
}

interface TreeNode {
  name: string;
  children?: TreeNode[];
}

interface PathVisualizationProps {
  paths: PathData[];
  title?: string;
  className?: string;
  showDropdown?: boolean;
  initiatingNodeKey?: string;
}


export default function PathVisualization({ 
  paths, 
  title = "Paths", 
  className = "",
  showDropdown = true,
  initiatingNodeKey
}: PathVisualizationProps) {
  const [expanded, setExpanded] = useState(false);
  const [showGraph, setShowGraph] = useState(false);
  const [graphFullscreen, setGraphFullscreen] = useState(false);
  
  const { config } = useConfigWithRegion();
  const pathsCount = paths.length;

  // Process data for tree visualization
  const treeData = useMemo(() => {
    if (!showGraph || pathsCount === 0) return null;

    // Group messages by path similarity
    const pathGroups: PathGroup[] = [];
    
    paths.forEach(({ origin, pubkey, path }, index) => {
      // Parse path into 2-character slices and include pubkey as final hop
      const pathSlices = path.match(/.{1,2}/g) || [];
      const pubkeyPrefix = pubkey.substring(0, 2);
      const fullPathSlices = [...pathSlices, pubkeyPrefix];
      
      // Find existing group with same path structure
      const existingGroup = pathGroups.find(group => 
        group.pathSlices.length === fullPathSlices.length &&
        group.pathSlices.every((slice, i) => slice === fullPathSlices[i])
      );
      
      if (existingGroup) {
        existingGroup.indices.push(index);
      } else {
        pathGroups.push({
          path: path + pubkeyPrefix,
          pathSlices: fullPathSlices,
          indices: [index]
        });
      }
    });

    // Build tree structure for react-d3-tree
    const buildTree = (): TreeNode => {
      const rootName = initiatingNodeKey ? initiatingNodeKey.substring(0, 2) : "??";
      const root: TreeNode = { name: rootName, children: [] };
      
      pathGroups.forEach(group => {
        let currentNode = root;
        
        group.pathSlices.forEach((slice, level) => {
          let child = currentNode.children?.find(c => c.name === slice);
          
          if (!child) {
            child = { name: slice, children: [] };
            if (!currentNode.children) currentNode.children = [];
            currentNode.children.push(child);
          }
          
          currentNode = child;
        });
      });
      
      return root;
    };

    return buildTree();
  }, [showGraph, paths, pathsCount, initiatingNodeKey]);

  // Extract unique prefixes from tree data for name lookups
  const uniquePrefixes = useMemo(() => {
    if (!treeData) return [];
    
    const prefixes = new Set<string>();
    
    const extractPrefixes = (node: TreeNode) => {
      prefixes.add(node.name);
      node.children?.forEach(extractPrefixes);
    };
    
    extractPrefixes(treeData);
    return Array.from(prefixes);
  }, [treeData]);

  // Use the new useMeshcoreSearches hook to handle multiple prefix searches
  // Filter out "??" prefix and only search for valid hex prefixes
  const searches = useMemo(() => 
    uniquePrefixes
      .filter(prefix => prefix !== "??") // Don't search for placeholder prefix
      .map(prefix => ({
        query: prefix,
        exact: false,
        limit: 20,
        is_repeater: true, // Filter for repeaters only
        lastSeen: 60*60*24*2, // 2 days
        region: config?.selectedRegion,
        enabled: showGraph && prefix.length > 0
      }))
  , [uniquePrefixes, showGraph, config?.lastSeen, config?.selectedRegion]);

  const searchResults = useMeshcoreSearches({ searches });

  // Create mapping from prefix to node data (name + public key)
  const prefixToNodes = useMemo(() => {
    const mapping = new Map<string, Array<{ name: string; publicKey: string }>>();
    
    // Create searchable prefixes (excluding "??")
    const searchablePrefixes = uniquePrefixes.filter(prefix => prefix !== "??");
    
    searchablePrefixes.forEach((prefix, index) => {
      const searchResult = searchResults[index];
      if (searchResult?.data?.results) {
        const matchingNodes = searchResult.data.results
          .filter(result => result.public_key.toLowerCase().startsWith(prefix.toLowerCase()) && result.node_name)
          .map(result => ({
            name: result.node_name,
            publicKey: result.public_key
          }))
          .filter(node => node.name.length > 0);
        
        if (matchingNodes.length > 0) {
          mapping.set(prefix, matchingNodes);
        }
      }
    });
    
    return mapping;
  }, [searchResults, uniquePrefixes]);

  // Fixed node spacing optimized for ~3 lines of text
  const fixedNodeSize = useMemo(() => ({ x: 140, y: 100 }), []);

  const handleToggle = useCallback(() => {
    setExpanded(prev => !prev);
  }, []);

  const handleGraphToggle = useCallback(() => {
    setShowGraph(prev => !prev);
  }, []);

  const handleFullscreenToggle = useCallback(() => {
    setGraphFullscreen(prev => !prev);
  }, []);

  const PathsList = useCallback(() => (
    <div className="mt-1 p-2 bg-gray-100 dark:bg-neutral-700 rounded text-xs break-all text-gray-800 dark:text-gray-200">
      {paths.map(({ origin, pubkey, path }, index) => (
        <div key={index} className="flex items-center gap-2">
          <Link 
            href={`/meshcore/node/${pubkey}`}
            className="hover:underline cursor-pointer"
          >
            {origin}
          </Link>
          <PathDisplay 
            path={path} 
            origin_pubkey={pubkey} 
            className="text-xs"
          />
        </div>
      ))}
    </div>
  ), [paths]);

  // Memoize the render function to prevent unnecessary re-renders
  const renderCustomNodeElement = useCallback(({ nodeDatum, toggleNode }: any) => {
    const rootName = initiatingNodeKey ? initiatingNodeKey.substring(0, 2) : "??";
    const isRoot = nodeDatum.name === rootName;
    // Check if this node represents an origin pubkey (final 2-char hex from pubkey)
    const isOriginPubkey = paths.some(({ pubkey }) => {
      const pubkeyPrefix = pubkey.substring(0, 2);
      return nodeDatum.name === pubkeyPrefix;
    });
    
    // Get node data for this prefix
    const nodeData = prefixToNodes.get(nodeDatum.name) || [];
    const isResolved = nodeData.length > 0;

    
    return (
      <g key={`node-${nodeDatum.name}`}>
        {/* Use same circle style for all nodes */}
        <circle 
          r={15}
          fill={isRoot ? "#3b82f6" : "#6b7280"}
          stroke={isOriginPubkey && !isRoot ? "#10b981" : "none"}
          strokeWidth={isOriginPubkey && !isRoot ? 2 : 0}
        />
        
        {/* Hex prefix inside circle */}
        <text
          textAnchor="middle"
          y="5"
          style={{ fontSize: "10px", fill: "white", fontWeight: "bold", stroke: "none" }}
        >
          {nodeDatum.name}
        </text>
        
        {/* Show all node names below circle for resolved prefixes - clickable */}
        {isResolved && nodeData.map((node, index) => {
          // Calculate dynamic width based on text length (approximate 6px per character + padding)
          const estimatedWidth = Math.max(60, node.name.length * 8 + 20);
          return (
            <foreignObject
              key={index}
              x={-estimatedWidth / 2} // Center the link horizontally
              y={24 + (index * 12)} // Increased gap from circle to first name
              width={estimatedWidth}
              height={16}
            >
            <Link
              href={`/meshcore/node/${node.publicKey}`}
              className="block text-center font-bold hover:opacity-80 cursor-pointer whitespace-nowrap px-1 py-0.5 text-xs text-blue-600 dark:text-blue-300"
            >
              {node.name}
            </Link>
          </foreignObject>
          );
        })}
      </g>
    );
  }, [initiatingNodeKey, paths, prefixToNodes]);

  const GraphView = useCallback(() => {
    if (!showGraph || pathsCount === 0 || !treeData) return null;

    const renderTree = () => (
      <Tree
        key={`tree-${pathsCount}`}
        data={treeData}
        orientation="vertical"
        pathFunc="step"
        translate={{ x: graphFullscreen ? 300 : 200, y: graphFullscreen ? 80 : 50 }}
        separation={{ siblings: 1.2, nonSiblings: 1.5 }}
        nodeSize={fixedNodeSize}
        zoomable={true}
        draggable={true}
        renderCustomNodeElement={renderCustomNodeElement}
      />
    );

    if (graphFullscreen && typeof window !== 'undefined') {
      return createPortal(
        <div className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50">
          <div className="bg-white dark:bg-neutral-900 rounded-lg p-4 max-w-7xl max-h-4xl w-full h-full flex flex-col">
            <div className="flex justify-between items-center mb-4">
              <h3 className="text-lg font-medium text-gray-900 dark:text-gray-100">
                Path Visualization
              </h3>
              <button
                onClick={handleFullscreenToggle}
                className="p-2 hover:bg-gray-100 dark:hover:bg-neutral-800 rounded text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-100"
              >
                <ArrowsPointingInIcon className="w-5 h-5" />
              </button>
            </div>
            <div className="flex-1 overflow-hidden">
              <div className="w-full h-full border border-gray-200 dark:border-gray-700 rounded bg-neutral-200 dark:bg-neutral-800">
                {renderTree()}
              </div>
            </div>
          </div>
        </div>,
        document.body
      );
    }

    return (
      <div className="mt-2">
        <div className="flex justify-between items-center mb-2">
          <span className="text-sm font-medium text-gray-700 dark:text-gray-300">
            Path Graph
          </span>
          <button
            onClick={handleFullscreenToggle}
            className="p-1 hover:bg-gray-100 dark:hover:bg-neutral-800 rounded text-gray-600 dark:text-gray-300 hover:text-gray-800 dark:hover:text-gray-100"
          >
            <ArrowsPointingOutIcon className="w-4 h-4" />
          </button>
        </div>
        <div className="w-full h-64 border border-gray-200 dark:border-gray-700 rounded bg-neutral-200 dark:bg-neutral-800">
          {renderTree()}
        </div>
      </div>
    );
  }, [showGraph, pathsCount, treeData, graphFullscreen, handleFullscreenToggle, renderCustomNodeElement, fixedNodeSize]);

  if (!showDropdown) {
    return (
      <div className={className}>
        <div className="flex items-center gap-2 mb-2">
          <span className="text-sm text-gray-600 dark:text-gray-300">
            {pathsCount} path{pathsCount !== 1 ? 's' : ''}
          </span>
          {pathsCount > 0 && (
            <button
              onClick={handleGraphToggle}
              className="flex items-center gap-1 hover:text-gray-800 dark:hover:text-gray-100 transition-colors text-sm"
            >
              <span>{showGraph ? 'Hide Graph' : 'Show Graph'}</span>
            </button>
          )}
        </div>
        <PathsList />
        {showGraph && <GraphView />}
      </div>
    );
  }

  return (
    <div className={`text-sm text-gray-600 dark:text-gray-300 ${className}`}>
      <div className="flex items-center gap-2">
        <button
          onClick={handleToggle}
          className="flex items-center gap-1 hover:text-gray-800 dark:hover:text-gray-100 transition-colors"
        >
          <span>{pathsCount} path{pathsCount !== 1 ? 's' : ''}</span>
          <svg
            className={`w-3 h-3 transition-transform ${expanded ? 'rotate-180' : ''}`}
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
          </svg>
        </button>
        
        {pathsCount > 0 && (
          <button
            onClick={handleGraphToggle}
            className="flex items-center gap-1 hover:text-gray-800 dark:hover:text-gray-100 transition-colors"
          >
            <span>{showGraph ? 'Hide Graph' : 'Show Graph'}</span>
          </button>
        )}
      </div>
      
      {expanded && pathsCount > 0 && (
        <PathsList />
      )}
      
      <GraphView />
    </div>
  );
}
