import 'dart:math';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:cognithor_ui/l10n/generated/app_localizations.dart';
import 'package:cognithor_ui/providers/connection_provider.dart';
import 'package:cognithor_ui/theme/cognithor_theme.dart';
import 'package:cognithor_ui/widgets/neon_card.dart';

class KnowledgeGraphScreen extends StatefulWidget {
  const KnowledgeGraphScreen({super.key});

  @override
  State<KnowledgeGraphScreen> createState() => _KnowledgeGraphScreenState();
}

class _KnowledgeGraphScreenState extends State<KnowledgeGraphScreen> {
  List<Map<String, dynamic>> _entities = [];
  List<Map<String, dynamic>> _relations = [];
  bool _loading = true;
  String? _error;
  String _searchQuery = '';
  String _typeFilter = 'all';
  Map<String, dynamic>? _selectedEntity;
  List<Map<String, dynamic>> _selectedRelations = [];

  /// Pre-computed node positions shared between painter and hit-testing.
  Map<String, Offset> _nodePositions = {};

  /// The size used for the last layout computation.
  Size _lastLayoutSize = Size.zero;

  bool _initialized = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (!_initialized) {
      _initialized = true;
      _loadGraph();
    }
  }

  Future<void> _loadGraph() async {
    setState(() {
      _loading = true;
      _error = null;
    });
    try {
      final api = context.read<ConnectionProvider>().api;
      final result = await api.getMemoryGraphEntities();
      setState(() {
        _entities =
            (result['entities'] as List?)
                ?.map((e) => e as Map<String, dynamic>)
                .toList() ??
            [];
        _relations =
            (result['relations'] as List?)
                ?.map((e) => e as Map<String, dynamic>)
                .toList() ??
            [];
        _loading = false;
        _nodePositions = {};
        _lastLayoutSize = Size.zero;
      });
    } catch (e) {
      setState(() {
        _error = e.toString();
        _loading = false;
      });
    }
  }

  /// Compute force-directed layout positions for the current filtered entities.
  /// Results are cached until entities, relations, or canvas size change.
  void _computeLayout(Size size) {
    if (size == _lastLayoutSize && _nodePositions.isNotEmpty) return;

    final entities = _filteredEntities;
    if (entities.isEmpty) {
      _nodePositions = {};
      _lastLayoutSize = size;
      return;
    }

    final positions = <String, Offset>{};
    final rng = Random(42);

    // Initial circular placement
    for (var i = 0; i < entities.length; i++) {
      final id = (entities[i]['id'] ?? '$i').toString();
      final angle = 2 * pi * i / entities.length;
      final r = min(size.width, size.height) * 0.35;
      positions[id] = Offset(
        size.width / 2 + r * cos(angle) + (rng.nextDouble() - 0.5) * 20,
        size.height / 2 + r * sin(angle) + (rng.nextDouble() - 0.5) * 20,
      );
    }

    // Force-directed iterations
    for (var iter = 0; iter < 50; iter++) {
      final forces = <String, Offset>{};
      for (final e in entities) {
        forces[(e['id'] ?? '').toString()] = Offset.zero;
      }

      // Repulsion
      for (var i = 0; i < entities.length; i++) {
        for (var j = i + 1; j < entities.length; j++) {
          final idA = (entities[i]['id'] ?? '$i').toString();
          final idB = (entities[j]['id'] ?? '$j').toString();
          final delta = positions[idA]! - positions[idB]!;
          final dist = max(delta.distance, 1.0);
          final force = delta / dist * (5000 / (dist * dist));
          forces[idA] = forces[idA]! + force;
          forces[idB] = forces[idB]! - force;
        }
      }

      // Attraction (edges)
      for (final rel in _relations) {
        final src = (rel['source_id'] ?? rel['from'] ?? '').toString();
        final tgt = (rel['target_id'] ?? rel['to'] ?? '').toString();
        if (positions.containsKey(src) && positions.containsKey(tgt)) {
          final delta = positions[tgt]! - positions[src]!;
          final force = delta * 0.005;
          forces[src] = (forces[src] ?? Offset.zero) + force;
          forces[tgt] = (forces[tgt] ?? Offset.zero) - force;
        }
      }

      // Center gravity
      for (final e in entities) {
        final id = (e['id'] ?? '').toString();
        final center = Offset(size.width / 2, size.height / 2);
        forces[id] =
            (forces[id] ?? Offset.zero) + (center - positions[id]!) * 0.01;
      }

      // Apply with damping
      const damping = 0.9;
      for (final e in entities) {
        final id = (e['id'] ?? '').toString();
        positions[id] = positions[id]! + (forces[id] ?? Offset.zero) * damping;
        // Clamp to bounds
        positions[id] = Offset(
          positions[id]!.dx.clamp(20, size.width - 20),
          positions[id]!.dy.clamp(20, size.height - 20),
        );
      }
    }

    _nodePositions = positions;
    _lastLayoutSize = size;
  }

  /// Invalidate cached layout so it is recomputed on next paint.
  void _invalidateLayout() {
    _nodePositions = {};
    _lastLayoutSize = Size.zero;
  }

  Future<void> _selectEntity(Map<String, dynamic> entity) async {
    setState(() => _selectedEntity = entity);
    try {
      final api = context.read<ConnectionProvider>().api;
      final id = entity['id']?.toString() ?? '';
      final result = await api.getEntityRelations(id);
      setState(() {
        _selectedRelations =
            (result['relations'] as List?)
                ?.map((e) => e as Map<String, dynamic>)
                .toList() ??
            [];
      });
    } catch (_) {}
  }

  List<Map<String, dynamic>> get _filteredEntities {
    var list = _entities;
    if (_typeFilter != 'all') {
      list = list.where((e) => e['type'] == _typeFilter).toList();
    }
    if (_searchQuery.isNotEmpty) {
      final q = _searchQuery.toLowerCase();
      list = list
          .where(
            (e) => (e['name'] ?? e['label'] ?? '')
                .toString()
                .toLowerCase()
                .contains(q),
          )
          .toList();
    }
    return list;
  }

  static const _typeColors = CognithorTheme.entityColors;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final l = AppLocalizations.of(context);

    return Scaffold(
      appBar: AppBar(title: Text(l.knowledgeGraphTitle)),
      body: _loading
          ? const Center(child: CircularProgressIndicator())
          : _error != null
          ? Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(_error!, style: TextStyle(color: CognithorTheme.red)),
                  const SizedBox(height: 12),
                  ElevatedButton(onPressed: _loadGraph, child: Text(l.retry)),
                ],
              ),
            )
          : Row(
              children: [
                // Graph area
                Expanded(
                  flex: 3,
                  child: Column(
                    children: [
                      // Toolbar
                      Padding(
                        padding: const EdgeInsets.all(12),
                        child: Row(
                          children: [
                            Expanded(
                              child: TextField(
                                decoration: InputDecoration(
                                  hintText: l.searchEntities,
                                  prefixIcon: const Icon(Icons.search),
                                  isDense: true,
                                  contentPadding: const EdgeInsets.symmetric(
                                    horizontal: 12,
                                    vertical: 8,
                                  ),
                                ),
                                onChanged: (v) {
                                  setState(() => _searchQuery = v);
                                  _invalidateLayout();
                                },
                              ),
                            ),
                            const SizedBox(width: 8),
                            DropdownButton<String>(
                              value: _typeFilter,
                              items: [
                                DropdownMenuItem(
                                  value: 'all',
                                  child: Text(l.allTypes),
                                ),
                                ..._typeColors.keys.map(
                                  (t) => DropdownMenuItem(
                                    value: t,
                                    child: Text(t),
                                  ),
                                ),
                              ],
                              onChanged: (v) {
                                setState(() => _typeFilter = v ?? 'all');
                                _invalidateLayout();
                              },
                            ),
                          ],
                        ),
                      ),
                      // Legend
                      Padding(
                        padding: const EdgeInsets.symmetric(horizontal: 12),
                        child: Wrap(
                          spacing: 12,
                          children: _typeColors.entries
                              .map(
                                (e) => Row(
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    Container(
                                      width: 10,
                                      height: 10,
                                      decoration: BoxDecoration(
                                        color: e.value,
                                        shape: BoxShape.circle,
                                      ),
                                    ),
                                    const SizedBox(width: 4),
                                    Text(
                                      e.key,
                                      style: theme.textTheme.bodySmall,
                                    ),
                                  ],
                                ),
                              )
                              .toList(),
                        ),
                      ),
                      // Canvas
                      Expanded(
                        child: LayoutBuilder(
                          builder: (context, constraints) {
                            final canvasSize = Size(
                              constraints.maxWidth,
                              constraints.maxHeight,
                            );
                            _computeLayout(canvasSize);
                            return GestureDetector(
                              onTapDown: (details) {
                                _onTapGraph(details.localPosition);
                              },
                              child: CustomPaint(
                                size: canvasSize,
                                painter: _ForceGraphPainter(
                                  entities: _filteredEntities,
                                  relations: _relations,
                                  positions: _nodePositions,
                                  typeColors: _typeColors,
                                  brightness: theme.brightness,
                                ),
                              ),
                            );
                          },
                        ),
                      ),
                    ],
                  ),
                ),
                // Detail panel
                if (_selectedEntity != null)
                  SizedBox(
                    width: 280,
                    child: NeonCard(
                      tint: CognithorTheme.sectionAdmin,
                      borderRadius: 0,
                      child: _buildDetailPanel(theme),
                    ),
                  ),
              ],
            ),
    );
  }

  void _onTapGraph(Offset position) {
    if (_nodePositions.isEmpty) return;

    final entities = _filteredEntities;
    Map<String, dynamic>? closest;
    var minDist = double.infinity;

    for (final entity in entities) {
      final id = (entity['id'] ?? '').toString();
      final nodePos = _nodePositions[id];
      if (nodePos == null) continue;

      final dist = (nodePos - position).distance;
      if (dist < 20 && dist < minDist) {
        minDist = dist;
        closest = entity;
      }
    }

    if (closest != null) _selectEntity(closest);
  }

  Widget _buildDetailPanel(ThemeData theme) {
    final l = AppLocalizations.of(context);
    final e = _selectedEntity!;
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        Row(
          children: [
            Expanded(
              child: Text(
                (e['name'] ?? e['label'] ?? l.entities).toString(),
                style: theme.textTheme.titleLarge?.copyWith(fontSize: 16),
              ),
            ),
            IconButton(
              icon: const Icon(Icons.close, size: 18),
              onPressed: () => setState(() => _selectedEntity = null),
            ),
          ],
        ),
        const SizedBox(height: 8),
        _detailRow(theme, 'ID', (e['id'] ?? '-').toString()),
        _detailRow(
          theme,
          l.entityTypes,
          (e['type'] ?? l.unknownLabel).toString(),
        ),
        _detailRow(theme, l.confidence, (e['confidence'] ?? '-').toString()),
        if (e['attributes'] is Map) ...[
          const SizedBox(height: 12),
          Text(
            l.attributes,
            style: theme.textTheme.bodyMedium?.copyWith(
              fontWeight: FontWeight.w600,
            ),
          ),
          const SizedBox(height: 4),
          ...(e['attributes'] as Map).entries.map(
            (kv) => _detailRow(theme, kv.key.toString(), kv.value.toString()),
          ),
        ],
        if (_selectedRelations.isNotEmpty) ...[
          const SizedBox(height: 12),
          Text(
            '${l.relations} (${_selectedRelations.length})',
            style: theme.textTheme.bodyMedium?.copyWith(
              fontWeight: FontWeight.w600,
            ),
          ),
          const SizedBox(height: 4),
          ..._selectedRelations.map(
            (r) => Padding(
              padding: const EdgeInsets.only(bottom: 4),
              child: Container(
                padding: const EdgeInsets.all(8),
                decoration: BoxDecoration(
                  color: theme.scaffoldBackgroundColor,
                  borderRadius: BorderRadius.circular(6),
                ),
                child: Text(
                  '${r['type'] ?? 'relates'} -> ${r['target_name'] ?? r['target_id'] ?? '?'}',
                  style: theme.textTheme.bodySmall,
                ),
              ),
            ),
          ),
        ],
      ],
    );
  }

  Widget _detailRow(ThemeData theme, String label, String value) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 80,
            child: Text(label, style: theme.textTheme.bodySmall),
          ),
          Expanded(child: Text(value, style: theme.textTheme.bodyMedium)),
        ],
      ),
    );
  }
}

class _ForceGraphPainter extends CustomPainter {
  _ForceGraphPainter({
    required this.entities,
    required this.relations,
    required this.positions,
    required this.typeColors,
    this.brightness = Brightness.dark,
  });

  final List<Map<String, dynamic>> entities;
  final List<Map<String, dynamic>> relations;
  final Map<String, Offset> positions;
  final Map<String, Color> typeColors;
  final Brightness brightness;

  @override
  void paint(Canvas canvas, Size size) {
    if (entities.isEmpty || positions.isEmpty) return;

    // Draw edges
    final edgePaint = Paint()
      ..color =
          (brightness == Brightness.dark
                  ? CognithorTheme.textPrimary
                  : CognithorTheme.textTertiary)
              .withValues(alpha: 0.1)
      ..strokeWidth = 1;

    for (final rel in relations) {
      final src = (rel['source_id'] ?? rel['from'] ?? '').toString();
      final tgt = (rel['target_id'] ?? rel['to'] ?? '').toString();
      if (positions.containsKey(src) && positions.containsKey(tgt)) {
        canvas.drawLine(positions[src]!, positions[tgt]!, edgePaint);
      }
    }

    // Draw nodes
    for (final e in entities) {
      final id = (e['id'] ?? '').toString();
      final pos = positions[id];
      if (pos == null) continue;
      final type = (e['type'] ?? 'unknown').toString();
      final color = typeColors[type] ?? CognithorTheme.entityColors['unknown']!;
      final name = (e['name'] ?? e['label'] ?? '').toString();

      // Node circle
      canvas.drawCircle(pos, 8, Paint()..color = color.withValues(alpha: 0.3));
      canvas.drawCircle(
        pos,
        8,
        Paint()
          ..color = color
          ..style = PaintingStyle.stroke
          ..strokeWidth = 2,
      );

      // Label
      final tp = TextPainter(
        text: TextSpan(
          text: name.length > 12 ? '${name.substring(0, 12)}...' : name,
          style: TextStyle(
            color: brightness == Brightness.dark
                ? CognithorTheme.textSecondary
                : CognithorTheme.textTertiary,
            fontSize: 9,
          ),
        ),
        textDirection: TextDirection.ltr,
      )..layout(maxWidth: 80);
      tp.paint(canvas, Offset(pos.dx - tp.width / 2, pos.dy + 12));
    }
  }

  @override
  bool shouldRepaint(_ForceGraphPainter old) => true;
}
