// lib/widgets/activity/activity_map_page.dart
import 'dart:math' as math;

import 'package:aves/model/activity/activity_track.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/map/geo_map.dart';
import 'package:aves/widgets/common/providers/map_theme_provider.dart';
import 'package:aves_map/aves_map.dart';
import 'package:flutter/material.dart';

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

  final ActivityTrack activity;

  const ActivityMapPage({
    super.key,
    required this.activity,
  });

  @override
  State<ActivityMapPage> createState() => _ActivityMapPageState();
}

class _ActivityMapPageState extends State<ActivityMapPage> {
  final AvesMapController _mapController = AvesMapController();
  final ValueNotifier<bool> _isAnimatingNotifier = ValueNotifier(false);

  @override
  void dispose() {
    _mapController.dispose();
    _isAnimatingNotifier.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final points = widget.activity.validPoints;
    final initialCenter = widget.activity.boundsCenter ?? widget.activity.center;

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.activity.name),
      ),
      body: Column(
        children: [
          _ActivitySummaryHeader(activity: widget.activity),
          Expanded(
            // Importante: uso LayoutBuilder dentro l'Expanded, cosi calcolo lo
            // zoom sulla dimensione reale della mappa e non su tutto lo schermo.
            // Prima usavamo MediaQuery.sizeOf(context), che include AppBar e
            // header: su alcune tracce verticali calcolava uno zoom troppo alto.
            child: LayoutBuilder(
              builder: (context, constraints) {
                final mapSize = Size(
                  math.max(1, constraints.maxWidth),
                  math.max(1, constraints.maxHeight),
                );

                final initialZoom = _initialZoomForActivity(
                  widget.activity,
                  mapSize,
                  fallbackZoom: settings.infoMapZoom,
                );

                return MapTheme(
                  interactive: true,
                  showCoordinateFilter: false,
                  navigationButton: MapNavigationButton.back,
                  child: GeoMap(
                    controller: _mapController,
                    entries: const <AvesEntry>[],
                    availableSize: mapSize,
                    initialCenter: initialCenter,
                    initialZoom: points.length <= 1 ? settings.infoMapZoom : initialZoom,
                    isAnimatingNotifier: _isAnimatingNotifier,
                    tracks: widget.activity.tracks,
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }

  static double _initialZoomForActivity(
    ActivityTrack activity,
    Size mapSize, {
    required double fallbackZoom,
  }) {
    final points = activity.validPoints;
    if (points.length < 2) return fallbackZoom;

    final south = activity.minLatitude;
    final north = activity.maxLatitude;
    final west = activity.minLongitude;
    final east = activity.maxLongitude;

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

    final latSpan = (north - south).abs();
    final lonSpan = (east - west).abs();

    if (latSpan == 0 && lonSpan == 0) return fallbackZoom;

    // Padding reale in pixel. E' piu prevedibile di sottrarre valori fissi
    // all'altezza dello schermo, soprattutto con header/AppBar diversi.
    const paddingPixels = 72.0;
    final width = math.max(1.0, mapSize.width - paddingPixels * 2);
    final height = math.max(1.0, mapSize.height - paddingPixels * 2);

    const tileSize = 256.0;
    const maxZoom = 18.0;
    const minZoom = 1.0;

    final lonFraction = math.max(lonSpan / 360.0, 0.0000001);
    final zoomLon = math.log(width / (tileSize * lonFraction)) / math.ln2;

    final yNorth = _mercatorYFraction(north);
    final ySouth = _mercatorYFraction(south);
    final latFraction = math.max((ySouth - yNorth).abs(), 0.0000001);
    final zoomLat = math.log(height / (tileSize * latFraction)) / math.ln2;

    // Uso il piu basso dei due zoom, cosi entrano sia larghezza sia altezza.
    // Apro ancora un po' lo zoom per compensare padding interno/controlli mappa
    // e piccole differenze tra proiezione stimata e rendering effettivo.
    final rawZoom = math.min(zoomLon, zoomLat);
    final paddedZoom = rawZoom - 0.75;

    if (!paddedZoom.isFinite) return fallbackZoom;

    // Non apro mai piu zoomato del valore impostato dall'utente.
    final userMaxZoom = fallbackZoom.isFinite ? fallbackZoom : maxZoom;
    final maxAllowedZoom = math.min(maxZoom, userMaxZoom);

    return paddedZoom.clamp(minZoom, maxAllowedZoom).toDouble();
  }

  /// Coordinate Web Mercator normalizzate 0..1.
  static double _mercatorYFraction(double latitude) {
    final clampedLat = latitude.clamp(-85.05112878, 85.05112878).toDouble();
    final latRad = clampedLat * math.pi / 180.0;
    final mercator = math.log(math.tan(math.pi / 4.0 + latRad / 2.0));
    return (1.0 - mercator / math.pi) / 2.0;
  }
}

class _ActivitySummaryHeader extends StatelessWidget {
  final ActivityTrack activity;

  const _ActivitySummaryHeader({
    required this.activity,
  });

  @override
  Widget build(BuildContext context) {
    final distance = activity.distanceMeters;
    final duration = activity.duration;
    final elevationGain = activity.elevationGainMeters;

    return Material(
      color: Theme.of(context).colorScheme.surface,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        child: Wrap(
          spacing: 16,
          runSpacing: 8,
          children: [
            Text('Formato: ${activity.format}'),
            Text('${activity.pointCount} punti'),
            if (activity.sport != null) Text('Sport: ${activity.sport}'),
            if (distance != null)
              Text('Distanza: ${(distance / 1000).toStringAsFixed(2)} km'),
            if (elevationGain != null) Text('D+: ${elevationGain.round()} m'),
            if (duration != null) Text('Durata: ${_formatDuration(duration)}'),
          ],
        ),
      ),
    );
  }

  static String _formatDuration(Duration duration) {
    final hours = duration.inHours;
    final minutes = duration.inMinutes.remainder(60);
    final seconds = duration.inSeconds.remainder(60);

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

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

    return '${seconds}s';
  }
}
