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

import 'package:ar_flutter_plugin_updated/ar_flutter_plugin.dart';
import 'package:ar_flutter_plugin_updated/datatypes/config_planedetection.dart';
import 'package:ar_flutter_plugin_updated/datatypes/hittest_result_types.dart';
import 'package:ar_flutter_plugin_updated/datatypes/node_types.dart';
import 'package:ar_flutter_plugin_updated/managers/ar_anchor_manager.dart';
import 'package:ar_flutter_plugin_updated/managers/ar_location_manager.dart';
import 'package:ar_flutter_plugin_updated/managers/ar_object_manager.dart';
import 'package:ar_flutter_plugin_updated/managers/ar_session_manager.dart';
import 'package:ar_flutter_plugin_updated/models/ar_anchor.dart';
import 'package:ar_flutter_plugin_updated/models/ar_hittest_result.dart';
import 'package:ar_flutter_plugin_updated/models/ar_node.dart';
import 'package:flutter/material.dart';
import 'package:wisata_app/models/destination.dart';
import 'package:vector_math/vector_math_64.dart' as vector;

import '../features/ar/domain/entities/ar_experience.dart';
import '../features/ar/presentation/controllers/ar_scene_controller.dart';
import '../services/ar_model_asset_service.dart';
import '../services/destination_service.dart';

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

  static const routeName = '/ar';

  @override
  State<ArViewScreen> createState() => _ArViewScreenState();
}

class _ArViewScreenState extends State<ArViewScreen> {
  final ArModelAssetService _modelAssetService = const ArModelAssetService();
  final DestinationService _destinationService = const DestinationService();
  ARSessionManager? _sessionManager;
  ARObjectManager? _objectManager;
  ARAnchorManager? _anchorManager;
  ARNode? _placedNode;
  ARPlaneAnchor? _placedAnchor;

  ArExperience? _experience;
  ArSceneController? _sceneController;
  bool _isInitializing = true;
  bool _isPlacingModel = false;
  bool _isSessionReadyForPlacement = false;
  double _gestureStartScale = 0;
  double _gestureStartYaw = 0;
  Offset? _lastGestureFocalPoint;
  DateTime? _lastPlacementTapAt;
  String? _errorMessage;
  bool _didResolveRoute = false;

  bool get _hasPlacedObject => _sceneController?.hasPlacedObject ?? false;
  bool get _waitingForSurfaceTap =>
      _sceneController?.waitingForSurfaceTap ?? true;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (_didResolveRoute) return;
    _didResolveRoute = true;

    final routeArgument = ModalRoute.of(context)?.settings.arguments;
    unawaited(_resolveExperience(routeArgument));
  }

  Future<void> _resolveExperience(Object? routeArgument) async {
    try {
      if (routeArgument is Destination) {
        _setExperience(_experienceFromDestination(routeArgument));
      } else if (routeArgument is String) {
        final destination =
            await _destinationService.getDestinationDetail(routeArgument);
        if (destination == null) {
          throw StateError(
            'Destinasi dengan ID $routeArgument tidak ditemukan.',
          );
        }
        _setExperience(_experienceFromDestination(destination));
      } else {
        _errorMessage = 'Belum ada destinasi yang dipilih untuk pratinjau AR.';
      }
    } on StateError catch (error) {
      _errorMessage = error.message;
    } on Exception catch (error) {
      _errorMessage = error.toString();
    }

    if (!Platform.isAndroid) {
      _errorMessage =
          'ARCore tanpa marker hanya didukung pada perangkat Android.';
    }

    if (mounted) {
      setState(() {});
    }
  }

  void _setExperience(ArExperience experience) {
    _experience = experience;
    _sceneController = ArSceneController(experience);
  }

  ArExperience _experienceFromDestination(Destination destination) {
    if (destination.modelPath.isEmpty) {
      throw StateError('${destination.title} belum memiliki model AR.');
    }

    return ArExperience(
      destinationId: destination.id,
      destinationTitle: destination.title,
      modelPath: destination.modelPath,
      description: destination.arDescription,
    );
  }

  @override
  void dispose() {
    final node = _placedNode;
    final anchor = _placedAnchor;
    final objectManager = _objectManager;
    final anchorManager = _anchorManager;
    if (anchor != null && anchorManager != null) {
      unawaited(anchorManager.removeAnchor(anchor));
    } else if (node != null && objectManager != null) {
      unawaited(objectManager.removeNode(node));
    }
    _sessionManager?.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final experience = _experience;
    if (_errorMessage != null) {
      return _ArErrorView(
        message: _errorMessage ?? 'Mode AR belum bisa dibuka.',
      );
    }

    if (experience == null) {
      return const Scaffold(
        backgroundColor: Colors.black,
        body: Center(
          child: CircularProgressIndicator(color: Color(0xFFB7D05A)),
        ),
      );
    }

    return Scaffold(
      backgroundColor: Colors.black,
      body: Stack(
        children: [
          ARView(
            onARViewCreated: _onARViewCreated,
            planeDetectionConfig: PlaneDetectionConfig.horizontal,
          ),
          if (_hasPlacedObject) _buildGestureLayer(),
          _buildTopBar(experience),
          _buildInstructionOverlay(),
          if (_isInitializing || _isPlacingModel) _buildLoadingOverlay(),
          _buildControlDock(),
        ],
      ),
    );
  }

  void _onARViewCreated(
    ARSessionManager sessionManager,
    ARObjectManager objectManager,
    ARAnchorManager anchorManager,
    ARLocationManager locationManager,
  ) {
    _sessionManager = sessionManager;
    _objectManager = objectManager;
    _anchorManager = anchorManager;

    _sessionManager!.onInitialize(
      showAnimatedGuide: true,
      showFeaturePoints: false,
      showPlanes: true,
      showWorldOrigin: false,
      handleTaps: true,
      handlePans: false,
      handleRotation: false,
    );
    _objectManager!.onInitialize();
    _sessionManager!.onPlaneOrPointTap = _onPlaneOrPointTapped;
    _sessionManager!.onError = _handleArError;

    unawaited(_markSessionReadyAfterWarmUp());
  }

  Future<void> _markSessionReadyAfterWarmUp() async {
    await Future<void>.delayed(const Duration(milliseconds: 1600));
    if (!mounted) return;
    setState(() {
      _isInitializing = false;
      _isSessionReadyForPlacement = true;
    });
  }

  Future<void> _onPlaneOrPointTapped(
      List<ARHitTestResult> hitTestResults) async {
    final experience = _experience;
    final sceneController = _sceneController;
    if (_hasPlacedObject ||
        _isPlacingModel ||
        !_isSessionReadyForPlacement ||
        experience == null ||
        sceneController == null) {
      return;
    }

    final now = DateTime.now();
    final lastTap = _lastPlacementTapAt;
    if (lastTap != null &&
        now.difference(lastTap) < const Duration(milliseconds: 900)) {
      return;
    }
    _lastPlacementTapAt = now;

    final planeHit = _firstPlaneHit(hitTestResults);
    if (planeHit == null) {
      _showMessage(
          'Gerakkan ponsel perlahan sampai permukaan datar terdeteksi.');
      return;
    }

    setState(() {
      _isPlacingModel = true;
    });

    final anchor = ARPlaneAnchor(transformation: planeHit.worldTransform);
    try {
      final didAddAnchor = await _anchorManager?.addAnchor(anchor) ?? false;
      if (!didAddAnchor) {
        _finishPlacementWithError('Model belum bisa dikunci ke permukaan ini.');
        return;
      }

      await Future<void>.delayed(const Duration(milliseconds: 650));
      if (!mounted || !_isPlacingModel) {
        _anchorManager?.removeAnchor(anchor);
        return;
      }

      final localModelPath =
          await _modelAssetService.prepareGlbForAr(experience.modelPath);
      if (!mounted || !_isPlacingModel) {
        _anchorManager?.removeAnchor(anchor);
        return;
      }

      final node = ARNode(
        type: NodeType.fileSystemAppFolderGLB,
        uri: localModelPath,
        scale: vector.Vector3.all(sceneController.scale),
        position: vector.Vector3.zero(),
        eulerAngles: vector.Vector3(0, sceneController.yaw, 0),
      );

      final didAddNode =
          await _objectManager?.addNode(node, planeAnchor: anchor) ?? false;
      if (!didAddNode) {
        _anchorManager?.removeAnchor(anchor);
        _finishPlacementWithError('Model 3D lokal belum bisa dimuat.');
        return;
      }

      if (!mounted) return;
      setState(() {
        _placedAnchor = anchor;
        _placedNode = node;
        sceneController.markObjectPlaced();
        _isPlacingModel = false;
      });
    } on ArModelAssetException catch (exception) {
      _anchorManager?.removeAnchor(anchor);
      _finishPlacementWithError(exception.message);
    } catch (error) {
      debugPrint('[AR Model] Gagal menaruh ${experience.modelPath}: $error');
      _anchorManager?.removeAnchor(anchor);
      _finishPlacementWithError(
          'Sesi AR belum stabil. Coba pindai lantai lagi.');
    }
  }

  void _handleArError(String error) {
    debugPrint('[AR Model] Native AR error: $error');
    _finishPlacementWithError(error);
  }

  ARHitTestResult? _firstPlaneHit(List<ARHitTestResult> hits) {
    for (final hit in hits) {
      if (hit.type == ARHitTestResultType.plane) {
        return hit;
      }
    }
    return null;
  }

  void _finishPlacementWithError(String message) {
    if (!mounted) return;
    setState(() {
      _isPlacingModel = false;
    });
    _showMessage(message);
  }

  Widget _buildGestureLayer() {
    return Positioned.fill(
      child: GestureDetector(
        behavior: HitTestBehavior.translucent,
        onScaleStart: (details) {
          final sceneController = _sceneController;
          if (sceneController == null) return;
          _gestureStartScale = sceneController.scale;
          _gestureStartYaw = sceneController.yaw;
          _lastGestureFocalPoint = details.focalPoint;
        },
        onScaleUpdate: (details) {
          final node = _placedNode;
          if (node == null) return;

          if (details.pointerCount >= 2) {
            _setScale(
              _gestureStartScale * details.scale,
            );
            _setYaw(_gestureStartYaw + details.rotation, absolute: true);
            return;
          }

          final previousPoint = _lastGestureFocalPoint;
          if (previousPoint == null) return;
          final delta = details.focalPoint - previousPoint;
          final position = node.position;
          node.position = vector.Vector3(
            position.x + delta.dx * 0.001,
            position.y,
            position.z + delta.dy * 0.001,
          );
          _lastGestureFocalPoint = details.focalPoint;
        },
      ),
    );
  }

  Widget _buildTopBar(ArExperience experience) {
    return Positioned(
      left: 16,
      right: 16,
      top: 0,
      child: SafeArea(
        child: Padding(
          padding: const EdgeInsets.only(top: 12),
          child: Row(
            children: [
              IconButton.filledTonal(
                tooltip: 'Kembali',
                onPressed: () => Navigator.pop(context),
                icon: const Icon(Icons.arrow_back_rounded),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(
                      'Explore Lumajang',
                      style: Theme.of(context).textTheme.titleMedium?.copyWith(
                            color: Colors.white,
                            fontWeight: FontWeight.w900,
                          ),
                    ),
                    Text(
                      experience.destinationTitle,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: Theme.of(context).textTheme.bodySmall?.copyWith(
                            color: Colors.white.withValues(alpha: 0.78),
                            fontWeight: FontWeight.w600,
                          ),
                    ),
                  ],
                ),
              ),
              _StatusPill(hasObject: _hasPlacedObject),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildInstructionOverlay() {
    return Positioned(
      left: 18,
      right: 18,
      bottom: 142,
      child: AnimatedSwitcher(
        duration: const Duration(milliseconds: 220),
        child: Container(
          key: ValueKey('${_waitingForSurfaceTap}_$_hasPlacedObject'),
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 13),
          decoration: BoxDecoration(
            color: Colors.black.withValues(alpha: 0.58),
            borderRadius: BorderRadius.circular(18),
            border: Border.all(color: Colors.white.withValues(alpha: 0.16)),
          ),
          child: Row(
            children: [
              Icon(
                _hasPlacedObject
                    ? Icons.open_with_rounded
                    : Icons.grid_4x4_rounded,
                color: const Color(0xFFB7D05A),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Text(
                  _hasPlacedObject
                      ? 'Geser untuk memindahkan, putar dua jari untuk rotasi, cubit untuk memperbesar atau memperkecil.'
                      : 'Gerakkan ponsel perlahan untuk memindai permukaan, lalu ketuk bidang datar.',
                  style: const TextStyle(
                    color: Colors.white,
                    fontWeight: FontWeight.w700,
                    height: 1.35,
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildLoadingOverlay() {
    return Positioned.fill(
      child: IgnorePointer(
        child: DecoratedBox(
          decoration:
              BoxDecoration(color: Colors.black.withValues(alpha: 0.18)),
          child: const Center(
            child: CircularProgressIndicator(color: Color(0xFFB7D05A)),
          ),
        ),
      ),
    );
  }

  Widget _buildControlDock() {
    return Positioned(
      right: 16,
      bottom: 24,
      child: SafeArea(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            FloatingActionButton.small(
              heroTag: 'ar-reset',
              tooltip: 'Reset AR',
              onPressed: _resetScene,
              child: const Icon(Icons.restart_alt_rounded),
            ),
            const SizedBox(height: 12),
            FloatingActionButton.small(
              heroTag: 'ar-remove',
              tooltip: 'Hapus objek',
              onPressed: _hasPlacedObject ? _removeObject : null,
              child: const Icon(Icons.delete_outline_rounded),
            ),
            const SizedBox(height: 12),
            FloatingActionButton.small(
              heroTag: 'ar-rotate',
              tooltip: 'Putar objek',
              onPressed:
                  _hasPlacedObject ? () => _rotateObject(math.pi / 8) : null,
              child: const Icon(Icons.rotate_90_degrees_ccw_rounded),
            ),
            const SizedBox(height: 12),
            FloatingActionButton.small(
              heroTag: 'ar-zoom-in',
              tooltip: 'Perbesar',
              onPressed: _hasPlacedObject
                  ? () => _setScale((_sceneController?.scale ?? 0) + 0.04)
                  : null,
              child: const Icon(Icons.add_rounded),
            ),
            const SizedBox(height: 12),
            FloatingActionButton.small(
              heroTag: 'ar-zoom-out',
              tooltip: 'Perkecil',
              onPressed: _hasPlacedObject
                  ? () => _setScale((_sceneController?.scale ?? 0) - 0.04)
                  : null,
              child: const Icon(Icons.remove_rounded),
            ),
          ],
        ),
      ),
    );
  }

  void _rotateObject(double radians) {
    final node = _placedNode;
    if (node == null) return;
    _setYaw(radians);
  }

  void _setYaw(double radians, {bool absolute = false}) {
    final node = _placedNode;
    final sceneController = _sceneController;
    if (node == null || sceneController == null) return;
    final yaw = absolute
        ? sceneController.rotate(radians - sceneController.yaw)
        : sceneController.rotate(radians);
    node.eulerAngles = vector.Vector3(0, yaw, 0);
    setState(() {});
  }

  void _setScale(double nextScale) {
    final node = _placedNode;
    final sceneController = _sceneController;
    if (node == null || sceneController == null) return;
    final scale = sceneController.setScale(nextScale);
    node.scale = vector.Vector3.all(scale);
    setState(() {});
  }

  void _removeObject() {
    final anchor = _placedAnchor;
    if (anchor != null) {
      _anchorManager?.removeAnchor(anchor);
    } else if (_placedNode != null) {
      _objectManager?.removeNode(_placedNode!);
    }
    if (!mounted) return;
    setState(() {
      _placedAnchor = null;
      _placedNode = null;
      _sceneController?.markObjectRemoved();
    });
  }

  void _resetScene() {
    _removeObject();
    _sceneController?.reset();
    _showMessage(
        'Tampilan AR direset. Ketuk permukaan terdeteksi untuk mulai lagi.');
  }

  void _showMessage(String message) {
    if (!mounted) return;
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(SnackBar(content: Text(message)));
  }
}

class _StatusPill extends StatelessWidget {
  const _StatusPill({required this.hasObject});

  final bool hasObject;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
      decoration: BoxDecoration(
        color: hasObject
            ? const Color(0xFFB7D05A)
            : Colors.white.withValues(alpha: 0.18),
        borderRadius: BorderRadius.circular(999),
        border: Border.all(color: Colors.white.withValues(alpha: 0.18)),
      ),
      child: Text(
        hasObject ? 'Tampil' : 'Memindai',
        style: TextStyle(
          color: hasObject ? const Color(0xFF0B1F33) : Colors.white,
          fontWeight: FontWeight.w900,
          fontSize: 12,
        ),
      ),
    );
  }
}

class _ArErrorView extends StatelessWidget {
  const _ArErrorView({required this.message});

  final String message;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF7F4EA),
      body: SafeArea(
        child: Center(
          child: Padding(
            padding: const EdgeInsets.all(28),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Icon(Icons.phonelink_erase_rounded,
                    size: 64, color: Color(0xFFE04F3F)),
                const SizedBox(height: 18),
                Text(
                  'AR tidak tersedia',
                  style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                        fontWeight: FontWeight.w900,
                        color: const Color(0xFF0B1F33),
                      ),
                ),
                const SizedBox(height: 8),
                Text(
                  message,
                  textAlign: TextAlign.center,
                  style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                        color: const Color(0xFF405466),
                        height: 1.45,
                      ),
                ),
                const SizedBox(height: 24),
                FilledButton.icon(
                  onPressed: () => Navigator.pop(context),
                  icon: const Icon(Icons.arrow_back_rounded),
                  label: const Text('Kembali ke destinasi'),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
