import { useState, useMemo, useEffect } from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import {
  ReactFlow,
  Node,
  Edge,
  Background,
  Controls,
  MiniMap,
  useNodesState,
  useEdgesState,
  MarkerType,
  Position,
} from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import Dagre from '@dagrejs/dagre';
import { Database as DatabaseIcon, Loader2, Key, Link, RefreshCw } from 'lucide-react';
import { databasesApi, schemaApi, TableInfo, SchemaData } from '@/services/api';
import { Database } from '@/types';

// Custom node for table
function TableNode({ data }: { data: TableInfo & Record<string, unknown> }) {
  return (
    <div className="bg-white border-2 border-gray-300 rounded-lg shadow-lg min-w-64 max-w-80 overflow-hidden">
      {/* Table header */}
      <div className="bg-primary-600 text-white px-3 py-2 font-semibold text-sm">
        <div className="flex items-center gap-2">
          <DatabaseIcon size={14} />
          <span className="truncate">{data.name}</span>
        </div>
        {data.comment && (
          <div className="text-xs text-primary-200 mt-1 font-normal truncate" title={data.comment}>
            {data.comment}
          </div>
        )}
      </div>

      {/* Columns */}
      <div className="divide-y divide-gray-100 max-h-64 overflow-y-auto">
        {data.columns.map((col) => (
          <div
            key={col.name}
            className="px-3 py-1.5 text-xs flex items-center gap-2 hover:bg-gray-50 cursor-default"
            title={col.comment || undefined}
          >
            <div className="flex items-center gap-1 flex-shrink-0">
              {col.is_primary && <Key size={11} className="text-yellow-500" />}
              {data.foreign_keys.some(fk => fk.column === col.name) && (
                <Link size={11} className="text-blue-500" />
              )}
            </div>
            <span className={`font-medium truncate ${col.is_primary ? 'text-yellow-700' : 'text-gray-700'}`}>
              {col.name}
            </span>
            <span className="text-gray-400 truncate flex-1 text-right">{col.type}</span>
            {!col.nullable && <span className="text-red-400 text-[10px] flex-shrink-0">NN</span>}
          </div>
        ))}
        {data.columns.length === 0 && (
          <div className="px-3 py-2 text-xs text-gray-400 italic">No columns</div>
        )}
      </div>

      {/* FK summary */}
      {data.foreign_keys.length > 0 && (
        <div className="bg-blue-50 px-3 py-1.5 text-xs text-blue-600 border-t border-blue-100">
          {data.foreign_keys.length} FK
        </div>
      )}
    </div>
  );
}

const nodeTypes = {
  table: TableNode,
};

// Calculate node height based on columns count
function getNodeHeight(table: TableInfo): number {
  const headerHeight = 44;
  const columnHeight = 28;
  const fkBarHeight = table.foreign_keys.length > 0 ? 28 : 0;
  const maxVisibleColumns = 10;
  const visibleColumns = Math.min(table.columns.length, maxVisibleColumns);
  return headerHeight + (visibleColumns * columnHeight) + fkBarHeight + 8;
}

function getLayoutedElements(schema: SchemaData): { nodes: Node[]; edges: Edge[] } {
  const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({}));

  g.setGraph({
    rankdir: 'LR', // Left to Right
    nodesep: 80,   // Horizontal separation between nodes
    ranksep: 120,  // Vertical separation between ranks
    marginx: 50,
    marginy: 50,
  });

  // Add nodes
  schema.tables.forEach((table) => {
    const nodeId = `${table.schema}.${table.name}`;
    const width = 280;
    const height = getNodeHeight(table);
    g.setNode(nodeId, { width, height });
  });

  // Add edges
  schema.tables.forEach((table) => {
    const sourceId = `${table.schema}.${table.name}`;
    table.foreign_keys.forEach((fk) => {
      const targetTable = schema.tables.find(t => t.name === fk.references_table);
      if (targetTable) {
        const targetId = `${targetTable.schema}.${targetTable.name}`;
        g.setEdge(sourceId, targetId);
      }
    });
  });

  // Run layout
  Dagre.layout(g);

  // Create nodes with positions
  const nodes: Node[] = schema.tables.map((table) => {
    const nodeId = `${table.schema}.${table.name}`;
    const nodeWithPosition = g.node(nodeId);

    return {
      id: nodeId,
      type: 'table',
      position: {
        x: nodeWithPosition.x - nodeWithPosition.width / 2,
        y: nodeWithPosition.y - nodeWithPosition.height / 2,
      },
      data: { ...table } as TableInfo & Record<string, unknown>,
      sourcePosition: Position.Right,
      targetPosition: Position.Left,
    };
  });

  // Create edges
  const edges: Edge[] = [];
  schema.tables.forEach((table) => {
    const sourceId = `${table.schema}.${table.name}`;
    table.foreign_keys.forEach((fk) => {
      const targetTable = schema.tables.find(t => t.name === fk.references_table);
      if (targetTable) {
        const targetId = `${targetTable.schema}.${targetTable.name}`;
        edges.push({
          id: `${fk.constraint_name}`,
          source: sourceId,
          target: targetId,
          type: 'smoothstep',
          animated: true,
          style: { stroke: '#3b82f6', strokeWidth: 2 },
          markerEnd: {
            type: MarkerType.ArrowClosed,
            color: '#3b82f6',
          },
          label: `${fk.column} → ${fk.references_column}`,
          labelStyle: { fontSize: 10, fill: '#666' },
          labelBgStyle: { fill: 'white', fillOpacity: 0.9 },
          labelBgPadding: [4, 2] as [number, number],
        });
      }
    });
  });

  return { nodes, edges };
}

export default function DatabaseSchema() {
  const queryClient = useQueryClient();
  const [selectedDbId, setSelectedDbId] = useState<string>('');

  const { data: databases = [], isLoading: isLoadingDatabases } = useQuery({
    queryKey: ['databases'],
    queryFn: async () => {
      const { data } = await databasesApi.getAll();
      return data.filter((db: Database) => db.type !== 'aql');
    },
  });

  const { data: schemaResponse, isLoading: isLoadingSchema } = useQuery({
    queryKey: ['schema', selectedDbId],
    queryFn: async () => {
      const { data } = await schemaApi.getSchema(selectedDbId);
      return data;
    },
    enabled: !!selectedDbId,
  });

  const refreshMutation = useMutation({
    mutationFn: () => schemaApi.refreshSchema(selectedDbId),
    onSuccess: (response) => {
      queryClient.setQueryData(['schema', selectedDbId], response.data);
    },
  });

  const schema = schemaResponse?.data;

  const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => {
    if (!schema) return { nodes: [], edges: [] };
    return getLayoutedElements(schema);
  }, [schema]);

  const [nodes, setNodes, onNodesChange] = useNodesState<Node>([]);
  const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);

  // Update nodes when schema changes
  useEffect(() => {
    if (layoutedNodes.length > 0) {
      setNodes(layoutedNodes);
      setEdges(layoutedEdges);
    }
  }, [layoutedNodes, layoutedEdges, setNodes, setEdges]);

  if (isLoadingDatabases) {
    return (
      <div className="flex items-center justify-center h-64">
        <Loader2 className="animate-spin text-primary-600" size={32} />
      </div>
    );
  }

  return (
    <div className="flex flex-col -m-8 bg-white overflow-hidden" style={{ height: 'calc(100vh - 65px)' }}>
      {/* Toolbar */}
      <div className="flex items-center gap-4 p-3 border-b border-gray-200 bg-gray-50 flex-shrink-0">
        <div className="flex items-center gap-2">
          <DatabaseIcon size={18} className="text-gray-500" />
          <select
            value={selectedDbId}
            onChange={(e) => setSelectedDbId(e.target.value)}
            className="border border-gray-300 rounded-md px-3 py-1.5 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-primary-500 min-w-48"
          >
            <option value="">Выберите базу данных</option>
            {databases.map((db: Database) => (
              <option key={db.id} value={db.id}>
                {db.name} ({db.type})
              </option>
            ))}
          </select>
        </div>

        {selectedDbId && (
          <button
            onClick={() => refreshMutation.mutate()}
            disabled={refreshMutation.isPending}
            className="flex items-center gap-2 px-4 py-1.5 bg-primary-600 text-white rounded-md hover:bg-primary-700 disabled:opacity-50 text-sm font-medium"
          >
            {refreshMutation.isPending ? (
              <Loader2 className="animate-spin" size={16} />
            ) : (
              <RefreshCw size={16} />
            )}
            Обновить схему
          </button>
        )}

        {schema && (
          <div className="text-sm text-gray-500 ml-auto">
            {schema.tables.length} таблиц | Обновлено: {new Date(schema.updated_at).toLocaleString()}
          </div>
        )}
      </div>

      {/* Schema visualization */}
      <div className="flex-1">
        {!selectedDbId && (
          <div className="flex items-center justify-center h-full text-gray-400">
            Выберите базу данных для просмотра схемы
          </div>
        )}

        {selectedDbId && isLoadingSchema && (
          <div className="flex items-center justify-center h-full">
            <Loader2 className="animate-spin text-primary-600" size={32} />
          </div>
        )}

        {selectedDbId && !isLoadingSchema && schema && nodes.length > 0 && (
          <ReactFlow
            nodes={nodes}
            edges={edges}
            onNodesChange={onNodesChange}
            onEdgesChange={onEdgesChange}
            nodeTypes={nodeTypes}
            fitView
            fitViewOptions={{ padding: 0.2 }}
            minZoom={0.1}
            maxZoom={2}
            defaultEdgeOptions={{
              type: 'smoothstep',
            }}
          >
            <Background color="#e5e7eb" gap={16} />
            <Controls />
            <MiniMap
              nodeColor="#6366f1"
              maskColor="rgba(0, 0, 0, 0.1)"
              style={{ background: '#f3f4f6' }}
            />
          </ReactFlow>
        )}

        {selectedDbId && !isLoadingSchema && schema && schema.tables.length === 0 && (
          <div className="flex items-center justify-center h-full text-gray-400">
            В базе данных нет таблиц
          </div>
        )}
      </div>
    </div>
  );
}
