// lib/widgets/activity/activity_files_page.dart
import 'dart:io';
import 'dart:math' as math;
import 'dart:typed_data';

import 'package:aves/model/activity/activity_track.dart';
import 'package:aves/utils/activity_file_parser.dart';
import 'package:aves/widgets/activity/activity_map_page.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart' show Distance, LatLng, LengthUnit;

class ActivityFilesPage extends StatefulWidget {
  static const routeName = '/activity';

  const ActivityFilesPage({super.key});

  @override
  State<ActivityFilesPage> createState() => _ActivityFilesPageState();
}

class _ActivityFilesPageState extends State<ActivityFilesPage> {
  static final Distance _distance = Distance();

  final List<ActivityTrack> _activities = [];

  Future<void> _pickActivityFile() async {
    try {
      final result = await FilePicker.platform.pickFiles(
        type: FileType.any,
        withData: true,
      );

      final pickedFile = result?.files.single;
      if (pickedFile == null) return;

      if (!ActivityFileParser.isSupportedFileName(pickedFile.name)) {
        if (!mounted) return;
        _showMessage('Seleziona un file GPX, TCX o FIT.');
        return;
      }

      final bytes = await _readPickedFileBytes(pickedFile);

      final activity = await ActivityFileParser.parse(
        bytes,
        fileName: pickedFile.name,
      );

      if (activity == null) {
        if (!mounted) return;
        _showMessage('Impossibile leggere il file attività.');
        return;
      }

      if (activity.isEmpty) {
        if (!mounted) return;
        _showMessage('Nessuna traccia GPS valida trovata nel file.');
        return;
      }

      if (!mounted) return;

      setState(() {
        _activities.add(activity);
      });
    } catch (e) {
      if (!mounted) return;
      _showMessage('Impossibile leggere il file attività: $e');
    }
  }

  Future<Uint8List> _readPickedFileBytes(PlatformFile pickedFile) async {
    final bytes = pickedFile.bytes;
    if (bytes != null) return bytes;

    final path = pickedFile.path;
    if (path == null) {
      throw Exception('percorso file non disponibile');
    }

    return File(path).readAsBytes();
  }

  void _openActivity(ActivityTrack activity) {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => ActivityMapPage(activity: activity),
      ),
    );
  }

  void _showMessage(String message) {
    ScaffoldMessenger.maybeOf(context)?.showSnackBar(
      SnackBar(
        content: Text(message),
      ),
    );
  }

  void _showRecalculateDialog(ActivityTrack activity) {
    final fileDistance = activity.fileDistanceMeters ?? activity.distanceMeters;
    final fileElevation = activity.fileElevationGainMeters ?? activity.elevationGainMeters;

    final recomputedDistance = _computeDistanceFromTrack(activity);
    final recomputedElevation = activity.computedElevationGainMeters ??
        _readDiagnosticDouble(activity.diagnostics, 'computedElevationGainMeters');

    showDialog<void>(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: const Text('Confronto ricalcolo'),
          content: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(activity.displayName),
              const SizedBox(height: 12),
              _CompareLine(
                label: 'Distanza file',
                value: _formatDistance(fileDistance),
              ),
              _CompareLine(
                label: 'Distanza ricalcolata',
                value: _formatDistance(recomputedDistance),
              ),
              _CompareLine(
                label: 'Differenza distanza',
                value: _formatDistanceDelta(recomputedDistance, fileDistance),
              ),
              const Divider(height: 24),
              _CompareLine(
                label: 'D+ file',
                value: _formatElevation(fileElevation),
              ),
              _CompareLine(
                label: 'D+ ricalcolato',
                value: recomputedElevation == null
                    ? 'non disponibile dai punti salvati'
                    : _formatElevation(recomputedElevation),
              ),
              _CompareLine(
                label: 'Differenza D+',
                value: recomputedElevation == null
                    ? '-'
                    : _formatElevationDelta(recomputedElevation, fileElevation),
              ),
              if (recomputedElevation == null) ...[
                const SizedBox(height: 12),
                const Text(
                  'Nota: per ricalcolare il D+ servono le quote dei singoli punti. '
                  'Con il modello attuale la lista contiene solo LatLng; per FIT uso il valore calcolato dal parser quando disponibile.',
                  style: TextStyle(fontSize: 12),
                ),
              ],
            ],
          ),
          actions: [
            TextButton(
              onPressed: () => Navigator.of(context).pop(),
              child: const Text('Chiudi'),
            ),
          ],
        );
      },
    );
  }

  static double? _computeDistanceFromTrack(ActivityTrack activity) {
    final points = activity.validPoints;
    if (points.length < 2) return null;

    double total = 0;

    for (int i = 1; i < points.length; i++) {
      final segment = _distance.as(
        LengthUnit.Meter,
        points[i - 1],
        points[i],
      );

      if (!segment.isFinite || segment <= 0) continue;

      // Filtro semplice anti-spike per ricalcolo visuale nella lista.
      // Il parser FIT usa un algoritmo piu completo con timestamp/sport.
      if (segment > 500) continue;

      total += segment;
    }

    return total > 0 ? total : null;
  }

  static double? _readDiagnosticDouble(String? diagnostics, String key) {
    if (diagnostics == null || diagnostics.isEmpty) return null;

    for (final line in diagnostics.split('\n')) {
      final index = line.indexOf('=');
      if (index <= 0) continue;

      final currentKey = line.substring(0, index).trim();
      if (currentKey != key) continue;

      final value = line.substring(index + 1).trim();
      if (value == 'null' || value.isEmpty) return null;
      return double.tryParse(value.replaceAll(',', '.'));
    }

    return null;
  }

  static String _formatDistance(double? meters) {
    if (meters == null) return '-';
    return '${(meters / 1000).toStringAsFixed(2)} km';
  }

  static String _formatDistanceDelta(double? computed, double? file) {
    if (computed == null || file == null) return '-';
    final delta = computed - file;
    final sign = delta >= 0 ? '+' : '';
    return '$sign${(delta / 1000).toStringAsFixed(2)} km';
  }

  static String _formatElevation(double? meters) {
    if (meters == null) return '-';
    return '${meters.round()} m';
  }

  static String _formatElevationDelta(double? computed, double? file) {
    if (computed == null || file == null) return '-';
    final delta = computed - file;
    final sign = delta >= 0 ? '+' : '';
    return '$sign${delta.round()} m';
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Attività'),
        actions: [
          IconButton(
            icon: const Icon(Icons.add),
            tooltip: 'Apri file attività',
            onPressed: _pickActivityFile,
          ),
        ],
      ),
      body: _activities.isEmpty
          ? Center(
              child: TextButton.icon(
                onPressed: _pickActivityFile,
                icon: const Icon(Icons.upload_file),
                label: const Text('Apri file GPX, TCX o FIT'),
              ),
            )
          : ListView.separated(
              itemCount: _activities.length,
              separatorBuilder: (_, __) => const Divider(height: 1),
              itemBuilder: (context, index) {
                final activity = _activities[index];

                return ListTile(
                  contentPadding: const EdgeInsets.symmetric(
                    horizontal: 12,
                    vertical: 8,
                  ),
                  leading: ActivityTrackPreview(
                    activity: activity,
                    size: 64,
                  ),
                  title: Text(
                    activity.displayName,
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                  ),
                  subtitle: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(activity.dateLabel),
                      const SizedBox(height: 2),
                      Text(
                        [
                          activity.sportLabel,
                          activity.distanceLabel,
                          activity.elevationGainLabel,
                          activity.durationLabel,
                          '${activity.pointCount} punti',
                        ].join(' · '),
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      ),
                    ],
                  ),
                  trailing: IconButton(
                    icon: const Icon(Icons.calculate_outlined),
                    tooltip: 'Ricalcola e confronta',
                    onPressed: () => _showRecalculateDialog(activity),
                  ),
                  onTap: () => _openActivity(activity),
                );
              },
            ),
    );
  }
}

class ActivityTrackPreview extends StatelessWidget {
  final ActivityTrack activity;
  final double size;

  const ActivityTrackPreview({
    super.key,
    required this.activity,
    this.size = 64,
  });

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return ClipRRect(
      borderRadius: BorderRadius.circular(12),
      child: DecoratedBox(
        decoration: BoxDecoration(
          color: scheme.surfaceContainerHighest,
          border: Border.all(
            color: scheme.outlineVariant,
          ),
        ),
        child: SizedBox.square(
          dimension: size,
          child: CustomPaint(
            painter: _ActivityTrackPreviewPainter(
              tracks: activity.tracks,
              lineColor: scheme.primary,
              pointColor: scheme.secondary,
            ),
          ),
        ),
      ),
    );
  }
}

class _ActivityTrackPreviewPainter extends CustomPainter {
  final Set<List<LatLng>> tracks;
  final Color lineColor;
  final Color pointColor;

  const _ActivityTrackPreviewPainter({
    required this.tracks,
    required this.lineColor,
    required this.pointColor,
  });

  @override
  void paint(Canvas canvas, Size size) {
    final allPoints = tracks
        .expand((segment) => segment)
        .where(
          (point) =>
              point.latitude.isFinite &&
              point.longitude.isFinite &&
              point.latitude.abs() <= 90 &&
              point.longitude.abs() <= 180,
        )
        .toList();

    if (allPoints.isEmpty) {
      _drawEmpty(canvas, size);
      return;
    }

    final minLat = allPoints.map((p) => p.latitude).reduce((a, b) => a < b ? a : b);
    final maxLat = allPoints.map((p) => p.latitude).reduce((a, b) => a > b ? a : b);
    final minLon = allPoints.map((p) => p.longitude).reduce((a, b) => a < b ? a : b);
    final maxLon = allPoints.map((p) => p.longitude).reduce((a, b) => a > b ? a : b);

    final latSpan = math.max(0.000001, maxLat - minLat);
    final lonSpan = math.max(0.000001, maxLon - minLon);

    const padding = 8.0;
    final drawableWidth = math.max(1.0, size.width - padding * 2);
    final drawableHeight = math.max(1.0, size.height - padding * 2);
    final scale = math.min(drawableWidth / lonSpan, drawableHeight / latSpan);

    final usedWidth = lonSpan * scale;
    final usedHeight = latSpan * scale;
    final offsetX = padding + (drawableWidth - usedWidth) / 2;
    final offsetY = padding + (drawableHeight - usedHeight) / 2;

    Offset project(LatLng point) {
      final x = offsetX + (point.longitude - minLon) * scale;
      final y = offsetY + (maxLat - point.latitude) * scale;
      return Offset(x, y);
    }

    final paint = Paint()
      ..color = lineColor
      ..strokeWidth = 2.2
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round
      ..strokeJoin = StrokeJoin.round;

    for (final segment in tracks) {
      final validSegment = segment
          .where(
            (point) =>
                point.latitude.isFinite &&
                point.longitude.isFinite &&
                point.latitude.abs() <= 90 &&
                point.longitude.abs() <= 180,
          )
          .toList();

      if (validSegment.length < 2) continue;

      final path = Path()..moveTo(project(validSegment.first).dx, project(validSegment.first).dy);

      for (int i = 1; i < validSegment.length; i++) {
        final projected = project(validSegment[i]);
        path.lineTo(projected.dx, projected.dy);
      }

      canvas.drawPath(path, paint);
    }

    final start = project(allPoints.first);
    canvas.drawCircle(
      start,
      3.2,
      Paint()..color = pointColor,
    );
  }

  void _drawEmpty(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = lineColor.withOpacity(0.4)
      ..strokeWidth = 1.5
      ..style = PaintingStyle.stroke;

    canvas.drawLine(
      Offset(size.width * 0.25, size.height * 0.50),
      Offset(size.width * 0.75, size.height * 0.50),
      paint,
    );
  }

  @override
  bool shouldRepaint(covariant _ActivityTrackPreviewPainter oldDelegate) {
    return oldDelegate.tracks != tracks ||
        oldDelegate.lineColor != lineColor ||
        oldDelegate.pointColor != pointColor;
  }
}

class _CompareLine extends StatelessWidget {
  final String label;
  final String value;

  const _CompareLine({
    required this.label,
    required this.value,
  });

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 2),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Flexible(child: Text(label)),
          const SizedBox(width: 16),
          Text(
            value,
            style: const TextStyle(fontWeight: FontWeight.w600),
          ),
        ],
      ),
    );
  }
}
