// lib/widgets/viewer/visual/entry_page_view.dart
import 'dart:async';

import 'package:aves/app_mode.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/settings/enums/widget_outline.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/viewer/view_state.dart';
import 'package:aves/services/common/services.dart';
import 'package:aves/services/media/media_session_service.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/view/view.dart';
import 'package:aves/widgets/aves_app.dart';
import 'package:aves/widgets/common/action_mixins/feedback.dart';
import 'package:aves/widgets/common/basic/insets.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/home_widget.dart';
import 'package:aves/widgets/viewer/controls/controller.dart';
import 'package:aves/widgets/viewer/controls/notifications.dart';
import 'package:aves/widgets/viewer/hero.dart';
import 'package:aves/widgets/viewer/video/conductor.dart';
import 'package:aves/widgets/viewer/view/conductor.dart';
import 'package:aves/widgets/viewer/visual/error.dart';
import 'package:aves/widgets/viewer/visual/raster.dart';
import 'package:aves/widgets/viewer/visual/vector.dart';
import 'package:aves/widgets/viewer/visual/video/cover.dart';
import 'package:aves/widgets/viewer/visual/video/subtitle/subtitle.dart';
import 'package:aves/widgets/viewer/visual/video/swipe_action.dart';
import 'package:aves/widgets/viewer/visual/video/video_view.dart';
import 'package:aves_magnifier/aves_magnifier.dart';
import 'package:aves_model/aves_model.dart';
import 'package:decorated_icon/decorated_icon.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

// Factory remota per controller video unificato (remoti + locali tramite Conductor)
import 'package:aves/widgets/viewer/video/remote_video_factory.dart';

// ✅ NEW: Remote origin costante
import 'package:aves/remote/remote_origin.dart';

class EntryPageView extends StatefulWidget {
  final AvesEntry mainEntry, pageEntry;
  final ViewerController viewerController;
  final VoidCallback? onDisposed;

  static const decorationCheckSize = 20.0;
  static const rasterMaxScale = ScaleLevel(factor: 5);
  static const vectorMaxScale = ScaleLevel(factor: 25);

  const EntryPageView({
    super.key,
    required this.mainEntry,
    required this.pageEntry,
    required this.viewerController,
    this.onDisposed,
  });

  @override
  State<EntryPageView> createState() => _EntryPageViewState();
}

class _EntryPageViewState extends State<EntryPageView> with TickerProviderStateMixin {
  late ValueNotifier<ViewState> _viewStateNotifier;
  late AvesMagnifierController _magnifierController;
  final Set<StreamSubscription> _subscriptions = {};
  final ValueNotifier<Widget?> _actionFeedbackChildNotifier = ValueNotifier(null);
  OverlayEntry? _actionFeedbackOverlayEntry;

  AvesEntry get mainEntry => widget.mainEntry;
  AvesEntry get entry => widget.pageEntry;
  ViewerController get viewerController => widget.viewerController;

  bool get _isRemote => entry.origin == RemoteOrigin.value;

  @override
  void initState() {
    super.initState();
    _registerWidget(widget);
  }

  @override
  void didUpdateWidget(covariant EntryPageView oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.pageEntry != widget.pageEntry) {
      _unregisterWidget(oldWidget);
      _registerWidget(widget);
    }
  }

  @override
  void dispose() {
    _unregisterWidget(widget);
    widget.onDisposed?.call();
    _actionFeedbackChildNotifier.dispose();
    super.dispose();
  }

  void _registerWidget(EntryPageView widget) {
    final entry = widget.pageEntry;
    _viewStateNotifier = context.read<ViewStateConductor>().getOrCreateController(entry).viewStateNotifier;
    _magnifierController = AvesMagnifierController();
    _subscriptions.add(_magnifierController.stateStream.listen(_onViewStateChanged));
    _subscriptions.add(_magnifierController.scaleBoundariesStream.listen(_onViewScaleBoundariesChanged));
    if (entry.isVideo) {
      _subscriptions.add(mediaSessionService.mediaCommands.listen(_onMediaCommand));
    }

    // idempotente
    context.read<VideoConductor>().registerRemoteFactory(RemoteVideoControllerFactory());

    viewerController.startAutopilotAnimation(
      vsync: this,
      onUpdate: ({required scaleLevel}) {
        final boundaries = _magnifierController.scaleBoundaries;
        if (boundaries != null) {
          final scale = boundaries.scaleForLevel(scaleLevel);
          _magnifierController.update(scale: scale, source: ChangeSource.animation);
        }
      },
    );
  }

  void _unregisterWidget(EntryPageView oldWidget) {
    viewerController.stopAutopilotAnimation(vsync: this);
    _magnifierController.dispose();
    _subscriptions
      ..forEach((sub) => sub.cancel())
      ..clear();
  }

  @override
  Widget build(BuildContext context) {
    Widget child = AnimatedBuilder(
      animation: entry.visualChangeNotifier,
      builder: (context, child) {
        Widget? child;

        // Pipeline unificata:
        // - SVG → _buildSvgView()
        // - Video → _buildVideoView()
        // - Raster (locali + remoti) → _buildRasterView()
        if (entry.isSvg) {
          child = _buildSvgView();
        } else if (!entry.displaySize.isEmpty) {
          if (entry.isVideo) {
            child = _buildVideoView();
          } else if (entry.isDecodingSupported || _isRemote) {
            child = _buildRasterView();
          }
        }

        child ??= ErrorView(
          entry: entry,
          onTap: _onTap,
        );
        return child;
      },
    );

    if (!settings.viewerUseCutout) {
      child = SafeCutoutArea(
        child: ClipRect(child: child),
      );
    }

    final animate = context.select<Settings, bool>((v) => v.animate);
    if (animate) {
      child = Consumer<EntryHeroInfo?>(
        builder: (context, info, child) => Hero(
          tag: info != null && info.entry == mainEntry ? info.tag : hashCode,
          transitionOnUserGestures: true,
          child: child!,
        ),
        child: child,
      );
    }

    return child;
  }

  // RASTER (locali + remoti)
  Widget _buildRasterView() {
    return _buildMagnifier(
      applyScale: false,
      child: RasterImageView(
        entry: entry,
        viewStateNotifier: _viewStateNotifier,
        errorBuilder: (context, error, stackTrace) => ErrorView(
          entry: entry,
          onTap: _onTap,
        ),
      ),
    );
  }

  Widget _buildSvgView() {
    return _buildMagnifier(
      maxScale: EntryPageView.vectorMaxScale,
      scaleStateCycle: _vectorScaleStateCycle,
      applyScale: false,
      child: VectorImageView(
        entry: entry,
        viewStateNotifier: _viewStateNotifier,
        errorBuilder: (context, error, stackTrace) => ErrorView(
          entry: entry,
          onTap: _onTap,
        ),
      ),
    );
  }

  // === Helper: frame Magnifier per i VIDEO senza doppio SAR ===
  // - Remoto: usa remoteWidth/remoteHeight, swap se remoteRotation=90/270, NO SAR.
  // - Locale: usa il calcolo stock (applica SAR tramite entry.videoDisplaySize).
  Size _videoFrameSize(double? sar) {
    if (_isRemote) {
      double w = (entry.remoteWidth ?? entry.width).toDouble();
      double h = (entry.remoteHeight ?? entry.height).toDouble();
      final rot90 = ((entry.remoteRotation ?? 0) % 180) == 90;
      if (rot90) {
        final t = w;
        w = h;
        h = t;
      }
      if (w <= 0 || h <= 0) {
        final ds = entry.displaySize;
        if (!ds.isEmpty) return ds;
        return const Size(1920, 1080); // fallback prudente
      }
      return Size(w, h);
    }
    // Locali: logica stock Aves (SAR incluso)
    return entry.videoDisplaySize(sar);
  }

  Widget _buildVideoView() {
    final videoController = context.read<VideoConductor>().getController(entry);
    if (videoController == null) return const SizedBox();

    return ValueListenableBuilder<double?>(
      valueListenable: videoController.sarNotifier,
      builder: (context, sar, child) {
        // REMOTE: evita doppio SAR nel frame; VideoView applicherà il SAR internamente
        final videoDisplaySize = _videoFrameSize(sar);
        final isPureVideo = entry.isPureVideo;

        return Selector<Settings, (bool, bool, bool)>(
          selector: (context, s) => (
            isPureVideo && s.videoGestureDoubleTapTogglePlay,
            isPureVideo && s.videoGestureSideDoubleTapSeek,
            isPureVideo && s.videoGestureVerticalDragBrightnessVolume,
          ),
          builder: (context, s, child) {
            final (playGesture, seekGesture, useVerticalDragGesture) = s;
            final useTapGesture = playGesture || seekGesture;

            MagnifierDoubleTapCallback? onDoubleTap;
            MagnifierGestureScaleStartCallback? onScaleStart;
            MagnifierGestureScaleUpdateCallback? onScaleUpdate;
            MagnifierGestureScaleEndCallback? onScaleEnd;

            if (useTapGesture) {
              void _applyAction(EntryAction action, {IconData? Function()? icon}) {
                _actionFeedbackChildNotifier.value = DecoratedIcon(
                  icon?.call() ?? action.getIconData(),
                  size: 48,
                  color: Colors.white,
                  shadows: const [Shadow(color: Colors.black, blurRadius: 4)],
                );
                VideoActionNotification(
                  controller: videoController,
                  entry: entry,
                  action: action,
                ).dispatch(context);
              }

              onDoubleTap = (alignment) {
                final x = alignment.x;
                if (seekGesture) {
                  final sideRatio = _getSideRatio();
                  if (sideRatio != null) {
                    if (x < sideRatio) {
                      _applyAction(EntryAction.videoReplay10);
                      return true;
                    } else if (x > 1 - sideRatio) {
                      _applyAction(EntryAction.videoSkip10);
                      return true;
                    }
                  }
                }
                if (playGesture) {
                  _applyAction(
                    EntryAction.videoTogglePlay,
                    icon: () => videoController.isPlaying ? AIcons.pause : AIcons.play,
                  );
                  return true;
                }
                return false;
              };
            }

            if (useVerticalDragGesture) {
              SwipeAction? swipeAction;
              var move = Offset.zero;
              var dropped = false;
              double? startValue;
              ValueNotifier<double?>? valueNotifier;

              onScaleStart = (details, doubleTap, boundaries) {
                dropped = details.pointerCount > 1 || doubleTap;
                if (dropped) return;

                startValue = null;
                valueNotifier = ValueNotifier<double?>(null);
                final alignmentX = details.focalPoint.dx / boundaries.viewportSize.width;
                final action = alignmentX > .5 ? SwipeAction.volume : SwipeAction.brightness;
                action.get().then((v) => startValue = v);
                swipeAction = action;
                move = Offset.zero;
                _actionFeedbackOverlayEntry = OverlayEntry(
                  builder: (context) => SwipeActionFeedback(
                    action: action,
                    valueNotifier: valueNotifier!,
                  ),
                );
                Overlay.of(context).insert(_actionFeedbackOverlayEntry!);
              };
              onScaleUpdate = (details) {
                if (valueNotifier == null) return false;

                move += details.focalPointDelta;
                dropped |= details.pointerCount > 1;
                if (valueNotifier!.value == null) {
                  dropped |= MagnifierGestureRecognizer.isXPan(move);
                }
                if (dropped) return false;

                final _startValue = startValue;
                if (_startValue != null) {
                  final double value = (_startValue - move.dy / SwipeActionFeedback.height).clamp(0, 1);
                  valueNotifier!.value = value;
                  swipeAction?.set(value);
                }
                return true;
              };
              onScaleEnd = (details) {
                valueNotifier?.dispose();

                _actionFeedbackOverlayEntry
                  ?..remove()
                  ..dispose();
                _actionFeedbackOverlayEntry = null;
              };
            }

            Widget videoChild = Stack(
              children: [
                _buildMagnifier(
                  displaySize: videoDisplaySize,
                  onScaleStart: onScaleStart,
                  onScaleUpdate: onScaleUpdate,
                  onScaleEnd: onScaleEnd,
                  onDoubleTap: onDoubleTap,
                  child: VideoView(
                    entry: entry,
                    controller: videoController,
                  ),
                ),
                VideoSubtitles(
                  entry: entry,
                  controller: videoController,
                  viewStateNotifier: _viewStateNotifier,
                ),
                if (useTapGesture)
                  ValueListenableBuilder<Widget?>(
                    valueListenable: _actionFeedbackChildNotifier,
                    builder: (context, feedbackChild, child) => ActionFeedback(
                      child: feedbackChild,
                    ),
                  ),
              ],
            );
            if (useVerticalDragGesture) {
              final scope = MagnifierGestureDetectorScope.maybeOf(context);
              if (scope != null) {
                videoChild = scope.copyWith(
                  acceptPointerEvent: MagnifierGestureRecognizer.isYPan,
                  child: videoChild,
                );
              }
            }
            return Stack(
              fit: StackFit.expand,
              children: [
                videoChild,
                VideoCover(
                  mainEntry: mainEntry,
                  pageEntry: entry,
                  magnifierController: _magnifierController,
                  videoController: videoController,
                  videoDisplaySize: videoDisplaySize,
                  onTap: _onTap,
                  magnifierBuilder: (coverController, coverSize, videoCoverUriImage) => _buildMagnifier(
                    controller: coverController,
                    displaySize: coverSize,
                    onDoubleTap: onDoubleTap,
                    child: Image(image: videoCoverUriImage),
                  ),
                ),
              ],
            );
          },
        );
      },
    );
  }

  /// Wrapper del Magnifier con bound **remote‑friendly**
  Widget _buildMagnifier({
    AvesMagnifierController? controller,
    Size? displaySize,
    ScaleLevel maxScale = EntryPageView.rasterMaxScale,
    ScaleStateCycle scaleStateCycle = defaultScaleStateCycle,
    bool applyScale = true,
    MagnifierGestureScaleStartCallback? onScaleStart,
    MagnifierGestureScaleUpdateCallback? onScaleUpdate,
    MagnifierGestureScaleEndCallback? onScaleEnd,
    MagnifierDoubleTapCallback? onDoubleTap,
    required Widget child,
  }) {
    final isWallpaperMode = context.read<ValueNotifier<AppMode>>().value == AppMode.setWallpaper;

    const contained = ScaleLevel(ref: ScaleReference.contained);
    const covered = ScaleLevel(ref: ScaleReference.covered);

    // REMOTO:
    // - minScale sotto al fit per abilitare pinch‑out
    // - initialScale al fit (poi RasterImageView lo “cappa” a <= 1.0)
    final minScale = isWallpaperMode
        ? covered
        : (_isRemote ? const ScaleLevel(ref: ScaleReference.contained, factor: .5) : contained);
    final initialScale = _isRemote ? contained : viewerController.initialScale;

    return ValueListenableBuilder<bool>(
      valueListenable: AvesApp.canGestureToOtherApps,
      builder: (context, canGestureToOtherApps, childWidget) {
        return AvesMagnifier(
          key: Key('${entry.uri}_${entry.pageId}_${entry.dateModifiedMillis}'),
          controller: controller ?? _magnifierController,
          contentSize: displaySize ?? entry.displaySize,
          allowOriginalScaleBeyondRange: !isWallpaperMode,
          allowDoubleTap: _allowDoubleTap,
          minScale: minScale,
          maxScale: maxScale,
          initialScale: initialScale,
          scaleStateCycle: scaleStateCycle,
          applyScale: applyScale,
          onScaleStart: onScaleStart,
          onScaleUpdate: onScaleUpdate,
          onScaleEnd: onScaleEnd,
          onFling: _onFling,
          onTap: (context, _, alignment, _) {
            if (context.mounted) {
              _onTap(alignment: alignment);
            }
          },
          onLongPress: canGestureToOtherApps ? _startGlobalDrag : null,
          onDoubleTap: onDoubleTap,
          child: childWidget!,
        );
      },
      child: child,
    );
  }

  Future<void> _startGlobalDrag() async {
    const dragShadowSize = Size.square(128);
    final cornerRadiusPx = await deviceService.getWidgetCornerRadiusPx();

    final devicePixelRatio = MediaQuery.devicePixelRatioOf(context);
    final brightness = Theme.of(context).brightness;
    final outline = await WidgetOutline.systemBlackAndWhite.color(brightness);

    final dragShadowBytes = await HomeWidgetPainter(
      entry: entry,
      devicePixelRatio: devicePixelRatio,
    ).drawWidget(
      sizeDip: dragShadowSize,
      cornerRadiusPx: cornerRadiusPx,
      outline: outline,
      shape: WidgetShape.rrect,
    );

    await windowService.startGlobalDrag(entry.uri, entry.bestTitle, dragShadowSize, dragShadowBytes);
  }

  void _onFling(AxisDirection direction) {
    const animate = true;
    switch (direction) {
      case AxisDirection.left:
        (context.isRtl ? const ShowNextEntryNotification(animate: animate) : const ShowPreviousEntryNotification(animate: animate)).dispatch(context);
      case AxisDirection.right:
        (context.isRtl ? const ShowPreviousEntryNotification(animate: animate) : const ShowNextEntryNotification(animate: animate)).dispatch(context);
      case AxisDirection.up:
        PopVisualNotification().dispatch(context);
      case AxisDirection.down:
        ShowInfoPageNotification().dispatch(context);
    }
  }

  Notification? _handleSideSingleTap(Alignment? alignment) {
    if (settings.viewerGestureSideTapNext && alignment != null) {
      final x = alignment.x;
      final sideRatio = _getSideRatio();
      if (sideRatio != null) {
        const animate = false;
        if (x < sideRatio) {
          return context.isRtl ? const ShowNextEntryNotification(animate: animate) : const ShowPreviousEntryNotification(animate: animate);
        } else if (x > 1 - sideRatio) {
          return context.isRtl ? const ShowPreviousEntryNotification(animate: animate) : const ShowNextEntryNotification(animate: animate);
        }
      }
    }
    return null;
  }

  void _onTap({Alignment? alignment}) => (_handleSideSingleTap(alignment) ?? const ToggleOverlayNotification()).dispatch(context);

  bool _allowDoubleTap(Alignment alignment) {
    if (entry.isVideo && settings.videoGestureSideDoubleTapSeek) {
      return true;
    }
    final actionNotification = _handleSideSingleTap(alignment);
    return actionNotification == null;
  }

  void _onMediaCommand(MediaCommandEvent event) {
    final videoController = context.read<VideoConductor>().getController(entry);
    if (videoController == null) return;

    switch (event.command) {
      case MediaCommand.play:
        videoController.play();
      case MediaCommand.pause:
        videoController.pause();
      case MediaCommand.skipToNext:
        ShowNextVideoNotification().dispatch(context);
      case MediaCommand.skipToPrevious:
        ShowPreviousVideoNotification().dispatch(context);
      case MediaCommand.stop:
        videoController.pause();
      case MediaCommand.seek:
        if (event is MediaSeekCommandEvent) {
          videoController.seekTo(event.position);
        }
    }
  }

  void _onViewStateChanged(MagnifierState v) {
    if (!mounted) return;
    _viewStateNotifier.value = _viewStateNotifier.value.copyWith(
      position: v.position,
      scale: v.scale,
    );
  }

  void _onViewScaleBoundariesChanged(ScaleBoundaries v) {
    _viewStateNotifier.value = _viewStateNotifier.value.copyWith(
      viewportSize: v.viewportSize,
      contentSize: v.contentSize,
    );
  }

  double? _getSideRatio() {
    if (!mounted) return null;
    final isPortrait = MediaQuery.orientationOf(context) == Orientation.portrait;
    return isPortrait ? 1 / 6 : 1 / 8;
  }

  static ScaleState _vectorScaleStateCycle(ScaleState actual) {
    switch (actual) {
      case ScaleState.initial:
        return ScaleState.covering;
      default:
        return ScaleState.initial;
    }
  }
}
