// lib/model/activity/activity_track.dart
import 'package:latlong2/latlong.dart';

class ActivityTrack {
  final String name;
  final String format;
  final Set<List<LatLng>> tracks;

  /// Nome percorso/attività letto dal file, se disponibile.
  /// Se non disponibile, la UI può usare [name].
  final String? routeName;

  /// Numero punti validi letti dal file prima di eventuale semplificazione grafica.
  final int pointCount;

  /// Valori usati normalmente dalla UI.
  final double? distanceMeters;
  final double? elevationGainMeters;

  /// Valori aggregati scritti nel file, ad esempio FIT totalDistance/totalAscent.
  final double? fileDistanceMeters;
  final double? fileElevationGainMeters;

  /// Valori ricalcolati dai punti del file, quando disponibili.
  /// Per l'elevazione serve che il parser mantenga/calcoli le quote: con soli
  /// LatLng non è possibile ricalcolare il D+ in questa pagina.
  final double? computedDistanceMeters;
  final double? computedElevationGainMeters;

  final Duration? duration;
  final DateTime? startTime;
  final String? sport;
  final String? diagnostics;

  const ActivityTrack({
    required this.name,
    required this.format,
    required this.tracks,
    required this.pointCount,
    this.routeName,
    this.distanceMeters,
    this.elevationGainMeters,
    this.fileDistanceMeters,
    this.fileElevationGainMeters,
    this.computedDistanceMeters,
    this.computedElevationGainMeters,
    this.duration,
    this.startTime,
    this.sport,
    this.diagnostics,
  });

  bool get isEmpty => tracks.isEmpty || tracks.every((segment) => segment.isEmpty);

  String get displayName {
    final value = routeName?.trim();
    if (value != null && value.isNotEmpty) return value;
    return name;
  }

  Iterable<LatLng> get points sync* {
    for (final segment in tracks) {
      yield* segment;
    }
  }

  List<LatLng> get validPoints {
    return points
        .where(
          (point) =>
              point.latitude.isFinite &&
              point.longitude.isFinite &&
              point.latitude.abs() <= 90 &&
              point.longitude.abs() <= 180,
        )
        .toList(growable: false);
  }

  int get displayPointCount => validPoints.length;

  LatLng? get center {
    final allPoints = validPoints;
    if (allPoints.isEmpty) return null;

    final latitude = allPoints.fold<double>(
          0,
          (sum, point) => sum + point.latitude,
        ) /
        allPoints.length;

    final longitude = allPoints.fold<double>(
          0,
          (sum, point) => sum + point.longitude,
        ) /
        allPoints.length;

    return LatLng(latitude, longitude);
  }

  double? get minLatitude {
    final allPoints = validPoints;
    if (allPoints.isEmpty) return null;

    return allPoints
        .map((point) => point.latitude)
        .reduce((a, b) => a < b ? a : b);
  }

  double? get maxLatitude {
    final allPoints = validPoints;
    if (allPoints.isEmpty) return null;

    return allPoints
        .map((point) => point.latitude)
        .reduce((a, b) => a > b ? a : b);
  }

  double? get minLongitude {
    final allPoints = validPoints;
    if (allPoints.isEmpty) return null;

    return allPoints
        .map((point) => point.longitude)
        .reduce((a, b) => a < b ? a : b);
  }

  double? get maxLongitude {
    final allPoints = validPoints;
    if (allPoints.isEmpty) return null;

    return allPoints
        .map((point) => point.longitude)
        .reduce((a, b) => a > b ? a : b);
  }

  bool get hasBounds =>
      minLatitude != null &&
      maxLatitude != null &&
      minLongitude != null &&
      maxLongitude != null;

  LatLng? get southWest {
    final south = minLatitude;
    final west = minLongitude;

    if (south == null || west == null) return null;
    return LatLng(south, west);
  }

  LatLng? get northEast {
    final north = maxLatitude;
    final east = maxLongitude;

    if (north == null || east == null) return null;
    return LatLng(north, east);
  }

  LatLng? get boundsCenter {
    final south = minLatitude;
    final north = maxLatitude;
    final west = minLongitude;
    final east = maxLongitude;

    if (south == null || north == null || west == null || east == null) {
      return null;
    }

    return LatLng(
      (south + north) / 2,
      (west + east) / 2,
    );
  }

  String get distanceLabel {
    final value = distanceMeters;
    if (value == null) return '- km';
    return '${(value / 1000).toStringAsFixed(2)} km';
  }

  String get elevationGainLabel {
    final value = elevationGainMeters;
    if (value == null) return 'D+ -';
    return 'D+ ${value.round()} m';
  }

  String get dateLabel {
    final value = startTime;
    if (value == null) return 'Data -';

    final local = value.toLocal();
    final day = local.day.toString().padLeft(2, '0');
    final month = local.month.toString().padLeft(2, '0');
    final year = local.year.toString().padLeft(4, '0');
    final hour = local.hour.toString().padLeft(2, '0');
    final minute = local.minute.toString().padLeft(2, '0');

    return '$day/$month/$year $hour:$minute';
  }

  String get durationLabel {
    final value = duration;
    if (value == null) return '-';

    final hours = value.inHours;
    final minutes = value.inMinutes.remainder(60);
    final seconds = value.inSeconds.remainder(60);

    if (hours > 0) {
      return '${hours}h ${minutes}m';
    }

    if (minutes > 0) {
      return '${minutes}m ${seconds}s';
    }

    return '${seconds}s';
  }

  String get sportLabel {
    final value = sport;
    if (value == null || value.trim().isEmpty) return 'Attività';
    return value;
  }
}
