import 'dart:async';
import 'dart:io' as io;
import 'dart:math';

import 'package:camera/camera.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:intl/intl.dart';
import 'dart:ui' as ui;
import 'package:latlong2/latlong.dart' as lat;
import 'image_and_location_data.dart'; // Standard local import
import 'components/location_details_widget.dart'; // Standard local import
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:path_provider/path_provider.dart';

// Callback function type for capturing image and location data
typedef ImageAndLocationCallback = void Function(ImageAndLocationData data);

class MapCameraLocation extends StatefulWidget {
  final CameraDescription camera;
  final ImageAndLocationCallback? onImageCaptured;
  final String userAgent;
  final String packageName;
  final double? latitude;
  final double? longitude;

  const MapCameraLocation({
    super.key,
    required this.camera,
    this.onImageCaptured,
    required this.userAgent,
    required this.packageName,
    this.latitude,
    this.longitude,
  });

  @override
  State<MapCameraLocation> createState() => _MapCameraLocationState();
}

class _MapCameraLocationState extends State<MapCameraLocation> {
  late CameraController _controller;
  late Future<void> _initializeControllerFuture;
  late AlignOnUpdate _followOnLocationUpdate;
  late StreamController<double?> _followCurrentLocationStreamController;

  io.File? cameraImagePath;
  io.File? ssImage;
  String? dateTime;
  final globalKey = GlobalKey();

  Placemark? placeMark;
  LocationData? locationData;

  ImageAndLocationData getImageAndLocationData() {
    return ImageAndLocationData(
      imagePath: cameraImagePath?.path,
      locationData: locationData,
    );
  }

  Timer? _positionTimer;

  @override
  void initState() {
    super.initState();

    // Support manual location injection for "Source of Truth" from Home screen
    if (widget.latitude != null && widget.longitude != null) {
      locationData = LocationData(
        latitude: widget.latitude.toString(),
        longitude: widget.longitude.toString(),
        locationName: 'Fetching address...',
        subLocation: '',
      );
    }

    // Initialize camera
    _controller = CameraController(
      widget.camera,
      ResolutionPreset.medium,
    );
    _initializeControllerFuture = _controller.initialize();
    _followOnLocationUpdate = AlignOnUpdate.always;
    _followCurrentLocationStreamController = StreamController<double?>();

    // Initial fetch and start periodic updates
    // Reduce frequency to 5 seconds to be more battery/CPU efficient
    Future.microtask(() => updatePosition(context));
    _positionTimer = Timer.periodic(const Duration(seconds: 5), (timer) {
      if (mounted) {
        updatePosition(context);
      }
    });

    dateTime = DateFormat.yMd().add_jm().format(DateTime.now());
  }

  @override
  void dispose() {
    _controller.dispose();
    _followCurrentLocationStreamController.close();
    _positionTimer?.cancel();
    super.dispose();
  }

  @override
  void setState(fn) {
    if (mounted) {
      super.setState(fn);
    } else {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          super.setState(fn);
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: FutureBuilder<void>(
        future: _initializeControllerFuture,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            final currentLat = double.tryParse(locationData?.latitude ?? '0') ?? 0;
            final currentLng = double.tryParse(locationData?.longitude ?? '0') ?? 0;

            return Center(
              child: RepaintBoundary(
                key: globalKey,
                child: Stack(
                  children: [
                    CameraPreview(_controller),
                    Positioned(
                      left: 0,
                      right: 0,
                      bottom: 10,
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          if (locationData != null)
                            SizedBox(
                              height: 160,
                              child: Row(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Padding(
                                    padding: const EdgeInsets.symmetric(horizontal: 8.0),
                                    child: Card(
                                      elevation: 3,
                                      shape: RoundedRectangleBorder(
                                        borderRadius: BorderRadius.circular(8.0),
                                      ),
                                      child: SizedBox(
                                        width: 120,
                                        child: Padding(
                                          padding: const EdgeInsets.all(5.0),
                                          child: FlutterMap(
                                            options: MapOptions(
                                              initialCenter: lat.LatLng(currentLat, currentLng),
                                              initialZoom: 14.0,
                                              onPositionChanged: (pos, hasGesture) {
                                                if (hasGesture) {
                                                  setState(() => _followOnLocationUpdate = AlignOnUpdate.never);
                                                }
                                              },
                                            ),
                                            children: [
                                              TileLayer(
                                                urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                                                tileProvider: NetworkTileProvider(
                                                  headers: {'User-Agent': widget.userAgent},
                                                ),
                                                userAgentPackageName: widget.packageName,
                                                minZoom: 12,
                                              ),
                                              CurrentLocationLayer(
                                                alignPositionStream: _followCurrentLocationStreamController.stream,
                                                alignPositionOnUpdate: _followOnLocationUpdate,
                                                style: LocationMarkerStyle(
                                                  showHeadingSector: !kIsWeb,
                                                  showAccuracyCircle: !kIsWeb,
                                                ),
                                                headingStream: kIsWeb ? const Stream.empty() : null,
                                              ),
                                            ],
                                          ),
                                        ),
                                      ),
                                    ),
                                  ),
                                  Expanded(
                                    child: LocationDetailsWidget(
                                      locationData: locationData,
                                      dateTime: dateTime,
                                    ),
                                  ),
                                  const SizedBox(width: 10)
                                ],
                              ),
                            )
                          else
                            const Center(child: CircularProgressIndicator(color: Colors.white)),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            );
          } else {
            return const Center(child: CircularProgressIndicator(color: Colors.white));
          }
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          try {
            await _initializeControllerFuture;
            await takeScreenshot();
          } catch (e) {
            if (kDebugMode) debugPrint('Capture Error: $e');
          }
        },
        child: const Icon(Icons.camera_alt),
      ),
    );
  }

  Future<void> takeScreenshot() async {
    final rng = Random();
    final boundary = globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
    final image = await boundary.toImage(pixelRatio: 2.0);
    final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    final pngBytes = byteData!.buffer.asUint8List();

    String finalPath = '';

    if (kIsWeb) {
      // On web we use the bytes to create a blob preview or similar
      // For now we just return an empty path or a fake one to avoid crash
      finalPath = 'web_capture_${rng.nextInt(10000)}.png';
    } else {
      final directory = (await getApplicationDocumentsDirectory()).path;
      final imgFile = io.File('$directory/screenshot${rng.nextInt(10000)}.png');
      await imgFile.writeAsBytes(pngBytes);
      finalPath = imgFile.path;
    }

    if (finalPath.isNotEmpty) {
      if (widget.onImageCaptured != null) {
        widget.onImageCaptured!(ImageAndLocationData(
          imagePath: finalPath,
          imageBytes: pngBytes,
          locationData: locationData,
        ));
      }
    }
  }

  Future<void> updatePosition(BuildContext context) async {
    try {
      double latVal;
      double lngVal;

      if (widget.latitude != null && widget.longitude != null) {
        latVal = widget.latitude!;
        lngVal = widget.longitude!;
      } else {
        final pos = await _determinePosition();
        latVal = pos.latitude;
        lngVal = pos.longitude;
      }

      // Reverse geocoding often doesn't work well on web without a provider
      if (kIsWeb) {
        setState(() {
          locationData = LocationData(
            latitude: latVal.toString(),
            longitude: lngVal.toString(),
            locationName: 'Web Location',
            subLocation: 'Browser Accuracy',
          );
        });
        return;
      }

      final placeMarks = await placemarkFromCoordinates(latVal, lngVal);

      if (mounted) {
        setState(() {
          if (placeMarks.isNotEmpty) {
            final p = placeMarks.first;
            locationData = LocationData(
              latitude: latVal.toString(),
              longitude: lngVal.toString(),
              locationName: "${p.locality ?? ""}, ${p.administrativeArea ?? ""}, ${p.country ?? ""}",
              subLocation: "${p.street ?? ""}, ${p.thoroughfare ?? ""} ${p.administrativeArea ?? ""}",
            );
          } else {
            locationData = LocationData(
              latitude: latVal.toString(),
              longitude: lngVal.toString(),
              locationName: 'Coordinate Found',
              subLocation: "No address details",
            );
          }
        });
      }
    } catch (e) {
      if (kDebugMode) debugPrint("Location Error: $e");
      if (locationData == null) {
        setState(() {
          locationData = LocationData(
            latitude: null,
            longitude: null,
            locationName: 'Error Loading Location',
            subLocation: e.toString(),
          );
        });
      }
    }
  }

  Future<Position> _determinePosition() async {
    // Basic service check
    if (!kIsWeb) {
      final serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) {
        final lastKnown = await Geolocator.getLastKnownPosition();
        if (lastKnown != null) return lastKnown;
        throw Exception('Location service disabled');
      }
    }

    var permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) {
        if (!kIsWeb) {
          final lastKnown = await Geolocator.getLastKnownPosition();
          if (lastKnown != null) return lastKnown;
        }
        throw Exception('Location permission denied');
      }
    }

    if (permission == LocationPermission.deniedForever) {
      if (!kIsWeb) {
        final lastKnown = await Geolocator.getLastKnownPosition();
        if (lastKnown != null) return lastKnown;
      }
      throw Exception('Location permission permanently denied');
    }

    try {
      return await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.medium,
        timeLimit: const Duration(seconds: 15),
      );
    } catch (e) {
      if (!kIsWeb) {
        final lastKnown = await Geolocator.getLastKnownPosition();
        if (lastKnown != null) return lastKnown;
      }
      rethrow;
    }
  }
}
