'use client';

import { Canvas } from '@react-three/fiber';
import {
  CameraControls,
  Line,
  Text,
} from '@react-three/drei';
import { Suspense, useEffect, useRef, useState } from 'react';
import { Stack, Text as MantineText, Paper, Title, Box, CloseButton, Group, Anchor, useComputedColorScheme } from '@mantine/core';
import { useRouter, usePathname, useSearchParams } from 'next/navigation';
import * as THREE from 'three';

// Define the shape of nodes and links from API
interface NodeData {
  id: string;
  title: string;
  body?: string;
  user_did: string;
  atp_uri: string;
  coords_3d: [number, number, number];
}

interface LinkData {
  in: string; // from node id
  out: string; // to node id
}

// 1. The 3D Node Component
function Node({
  node,
  isFocused,
  onNodeClick,
  isDark
}: {
  node: NodeData;
  isFocused: boolean;
  onNodeClick: (node: NodeData) => void;
  isDark: boolean;
}) {
  const [hovered, setHovered] = useState(false);

  const isExpanded = isFocused || hovered;
  const scale = isFocused ? 2.5 : 1;

  // Theme-aware colors
  const nodeColor = isDark ? '#e9ecef' : '#495057';
  const focusColor = '#4dabf7';
  const hoverColor = '#90c0ff';

  return (
    <mesh
      position={node.coords_3d}
      scale={scale}
      onClick={(e) => {
        e.stopPropagation();
        onNodeClick(node);
      }}
      onPointerOver={(e) => {
        e.stopPropagation();
        setHovered(true);
      }}
      onPointerOut={() => setHovered(false)}
    >
      <sphereGeometry args={[0.1, 32, 32]} />
      <meshStandardMaterial
        color={isFocused ? focusColor : (hovered ? hoverColor : nodeColor)}
        emissive={isFocused ? focusColor : (hovered ? hoverColor : nodeColor)}
        emissiveIntensity={isFocused ? 0.8 : (hovered ? 0.5 : 0.1)}
      />
      {/* Show title on hover or focus */}
      {isExpanded && (
        <Text
          position={[0, 0.3 / scale, 0]}
          fontSize={0.1 / scale}
          color={isDark ? 'white' : 'black'}
          anchorX="center"
          anchorY="middle"
        >
          {node.title}
        </Text>
      )}
    </mesh>
  );
}

// 2. The Main Scene Component
export function ThoughtGalaxy() {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const colorScheme = useComputedColorScheme('light');
  const isDark = colorScheme === 'dark';

  const [nodes, setNodes] = useState<NodeData[]>([]);
  const [links, setLinks] = useState<LinkData[]>([]);
  const [selectedNode, setSelectedNode] = useState<NodeData | null>(null);
  const cameraControlsRef = useRef<CameraControls>(null);
  const hasFitCamera = useRef(false);
  const hasFocusedNode = useRef<string | null>(null);

  // Get selectedNodeId from URL query params
  const selectedNodeId = searchParams.get('node');

  // Fetch data from API on mount and poll for updates
  useEffect(() => {
    async function fetchData() {
      try {
        const response = await fetch('/api/galaxy', {
          credentials: 'include', // Include cookies for authentication
        });

        if (!response.ok) {
          console.error('[ThoughtGalaxy] Failed to fetch galaxy data:', response.statusText);
          return;
        }

        const data = await response.json();

        if (data.message) {
          console.log('[ThoughtGalaxy]', data.message);
          // If calculating, poll again in 2 seconds
          setTimeout(fetchData, 2000);
          return;
        }

        setNodes(data.nodes || []);
        setLinks(data.links || []);

        console.log(`[ThoughtGalaxy] Loaded ${data.nodes?.length || 0} nodes and ${data.links?.length || 0} links`);
      } catch (error) {
        console.error('[ThoughtGalaxy] Error fetching data:', error);
      }
    }

    fetchData();
  }, []);

  // Function to fit camera to all nodes
  const fitCameraToNodes = () => {
    if (!cameraControlsRef.current || nodes.length === 0) {
      console.log('[ThoughtGalaxy] Cannot fit camera:', {
        hasRef: !!cameraControlsRef.current,
        nodesLength: nodes.length,
      });
      return;
    }

    console.log('[ThoughtGalaxy] Fitting camera to', nodes.length, 'nodes...');

    // Create a THREE.Box3 from node positions
    const box = new THREE.Box3();
    nodes.forEach((node) => {
      box.expandByPoint(new THREE.Vector3(
        node.coords_3d[0],
        node.coords_3d[1],
        node.coords_3d[2]
      ));
    });

    console.log('[ThoughtGalaxy] Bounding box:', {
      min: box.min,
      max: box.max,
      size: box.getSize(new THREE.Vector3()),
    });

    // Use CameraControls' built-in fitToBox method
    try {
      cameraControlsRef.current.fitToBox(
        box,
        false, // Don't animate on initial load
        { paddingLeft: 0.5, paddingRight: 0.5, paddingTop: 0.5, paddingBottom: 0.5 }
      );
      console.log('[ThoughtGalaxy] ✓ Camera fitted to bounds');
      hasFitCamera.current = true;
    } catch (error) {
      console.error('[ThoughtGalaxy] Error fitting camera:', error);
    }
  };

  // Fit camera when nodes change and we haven't fitted yet
  useEffect(() => {
    if (!hasFitCamera.current && nodes.length > 0 && !selectedNodeId) {
      // Only auto-fit if we're not focusing on a specific node
      // Try to fit after a short delay to ensure Canvas is ready
      const timer = setTimeout(() => {
        fitCameraToNodes();
      }, 100);
      return () => clearTimeout(timer);
    }
  }, [nodes, selectedNodeId]);

  // Auto-focus on specific node if selectedNodeId is provided via query params
  useEffect(() => {
    if (selectedNodeId && nodes.length > 0) {
      const focusNode = nodes.find((n) => n.id === selectedNodeId);
      if (focusNode) {
        console.log('[ThoughtGalaxy] Focusing on node:', selectedNodeId);

        // Always update selected node when selectedNodeId changes (don't wait for camera ref)
        setSelectedNode(focusNode);

        // Move camera if ref is available and we haven't focused this specific node yet
        if (cameraControlsRef.current && (!hasFocusedNode.current || hasFocusedNode.current !== selectedNodeId)) {
          cameraControlsRef.current.setLookAt(
            focusNode.coords_3d[0],
            focusNode.coords_3d[1],
            focusNode.coords_3d[2] + 2, // Position camera 2 units in front
            focusNode.coords_3d[0],
            focusNode.coords_3d[1],
            focusNode.coords_3d[2],
            hasFocusedNode.current ? true : false // Animate if not initial load
          );
          hasFocusedNode.current = selectedNodeId;
        }
      } else {
        // Node ID in URL doesn't exist in data, clear it
        setSelectedNode(null);
      }
    } else if (!selectedNodeId && selectedNode) {
      // Query param was cleared but we still have a selected node in state, clear it
      setSelectedNode(null);
      hasFocusedNode.current = null;
    }
  }, [selectedNodeId, nodes, selectedNode]);

  // Map links to node positions
  const linkLines = links
    .map((link) => {
      const startNode = nodes.find((n) => n.id === link.in);
      const endNode = nodes.find((n) => n.id === link.out);
      if (startNode && endNode) {
        return {
          start: startNode.coords_3d,
          end: endNode.coords_3d,
        };
      }
      return null;
    })
    .filter(Boolean) as { start: [number, number, number]; end: [number, number, number] }[];

  // Camera animation on node click
  const handleNodeClick = (node: NodeData) => {
    console.log('[ThoughtGalaxy] Node clicked:', node.id);

    // Set selected node immediately for responsive UI
    setSelectedNode(node);

    // Update URL with query param (this won't cause remount)
    const params = new URLSearchParams(searchParams);
    params.set('node', node.id);
    router.replace(`${pathname}?${params.toString()}`, { scroll: false });

    // Animate camera to node
    if (cameraControlsRef.current) {
      // Clear the focused node ref to ensure camera animates
      hasFocusedNode.current = null;
      cameraControlsRef.current.setLookAt(
        node.coords_3d[0],
        node.coords_3d[1],
        node.coords_3d[2] + 2,
        node.coords_3d[0],
        node.coords_3d[1],
        node.coords_3d[2],
        true // Animate
      );
    }
  };

  // Handle closing the modal
  const handleCloseModal = () => {
    console.log('[ThoughtGalaxy] Closing modal');
    setSelectedNode(null);

    // Remove node query param from URL
    const params = new URLSearchParams(searchParams);
    params.delete('node');
    const newSearch = params.toString();
    router.replace(`${pathname}${newSearch ? `?${newSearch}` : ''}`, { scroll: false });
  };

  console.log('[ThoughtGalaxy] Rendering with', nodes.length, 'nodes and', linkLines.length, 'link lines');

  // Show message if no nodes are ready yet
  if (nodes.length === 0) {
    return (
      <Stack align="center" justify="center" style={{ height: '100vh', width: '100vw' }}>
        <MantineText size="lg" c="dimmed">
          Create at least 3 nodes to visualize your thought galaxy
        </MantineText>
        <MantineText size="sm" c="dimmed">
          Nodes with content will automatically generate embeddings and 3D coordinates
        </MantineText>
      </Stack>
    );
  }

  return (
    <>
      {/* Floating content overlay for selected node */}
      {selectedNode && (
        <Box
          style={{
            position: 'absolute',
            top: '10px',
            left: '10px',
            right: '10px',
            zIndex: 1000,
            maxWidth: '600px',
            margin: '0 auto',
            maxHeight: 'calc(100vh - 100px)', // Leave room for top/bottom padding
          }}
        >
          <Paper p="md" radius="lg" withBorder shadow="xl" style={{ maxHeight: '100%', display: 'flex', flexDirection: 'column' }}>
            <Group justify="space-between" align="flex-start" mb="xs" style={{ flexShrink: 0 }}>
              <Box style={{ flex: 1, minWidth: 0 }}>
                <Title order={2} style={{ margin: 0, marginBottom: '0.25rem' }}>
                  {selectedNode.title}
                </Title>
                <Anchor
                  href={`https://bsky.app/profile/${selectedNode.user_did}/post/${selectedNode.atp_uri.split('/').pop()}`}
                  target="_blank"
                  rel="noopener noreferrer"
                  size="sm"
                  c="dimmed"
                >
                  View on Bluesky
                </Anchor>
              </Box>
              <CloseButton
                size="lg"
                onClick={handleCloseModal}
                aria-label="Close node details"
                style={{ flexShrink: 0 }}
              />
            </Group>
            {selectedNode.body && (
              <Box style={{ overflowY: 'auto', flex: 1, marginTop: '0.5rem' }}>
                <MantineText
                  size="md"
                  style={{
                    whiteSpace: 'pre-wrap',
                    lineHeight: 1.6,
                  }}
                >
                  {selectedNode.body}
                </MantineText>
              </Box>
            )}
          </Paper>
        </Box>
      )}

      <Canvas
        camera={{ position: [0, 5, 10], fov: 60 }}
        style={{ width: '100%', height: '100%' }}
        gl={{ preserveDrawingBuffer: true }}
        onCreated={(state) => {
          console.log('[ThoughtGalaxy] Canvas created successfully');
          // Try to fit camera now that scene is ready
          if (!hasFitCamera.current && nodes.length > 0) {
            setTimeout(() => fitCameraToNodes(), 50);
          }
        }}
      >
      {/* Theme-aware lighting */}
      <ambientLight intensity={isDark ? 0.3 : 0.5} />
      <pointLight position={[10, 10, 10]} intensity={isDark ? 0.8 : 1} />
      <CameraControls ref={cameraControlsRef} />

      <Suspense fallback={null}>
        <group>
          {/* Render all nodes */}
          {nodes.map((node) => (
            <Node
              key={node.id}
              node={node}
              isFocused={selectedNodeId === node.id || selectedNode?.id === node.id}
              onNodeClick={handleNodeClick}
              isDark={isDark}
            />
          ))}

          {/* Render all links - theme-aware colors */}
          {linkLines.map((line, i) => (
            <Line
              key={i}
              points={[line.start, line.end]}
              color={isDark ? '#495057' : '#adb5bd'}
              lineWidth={1}
            />
          ))}
        </group>
      </Suspense>
    </Canvas>
    </>
  );
}
