import 'dart:async';
import 'dart:io';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:screen_retriever/screen_retriever.dart';
import 'package:window_manager/window_manager.dart';

import '../../../shared/utils/app_logger.dart';

class FullscreenManager {
  static const _tag = 'Fullscreen';


  static bool get _isDesktop => Platform.isWindows || Platform.isMacOS;

  FullscreenManager({required this._onStateChanged});

  final VoidCallback _onStateChanged;

  bool _isFullscreen = false;
  bool _isChanging = false;
  Rect? _windowBoundsBeforeFullscreen;
  bool _wasMaximized = false;
  bool _wasResizable = true;
  bool _wasAlwaysOnTop = false;
  bool _isDisposed = false;
  Future<void>? _disposeRestoreFuture;

  bool get isFullscreen => _isFullscreen;

  Future<void> toggle() async {
    if (_isDisposed) return;
    if (!_isDesktop) return;
    if (_isChanging) return;
    _isChanging = true;
    final wasFullscreen = _isFullscreen;

    try {
      if (Platform.isWindows) {
        if (wasFullscreen) {
          await _leaveWindowsFullscreen();
        } else {
          await _enterWindowsFullscreen();
        }
      } else {
        await _toggleNativeFullscreen();
      }
    } catch (error) {
      AppLogger.warn(_tag, 'Failed to toggle', error);
      if (Platform.isWindows && !wasFullscreen) {
        try {
          await _leaveWindowsFullscreen(updateState: false);
        } catch (_) {}
        _setFullscreenState(false);
      } else if (!Platform.isWindows) {
        final actual = await _readNativeFullscreenState();
        if (actual != null) _setFullscreenState(actual);
      }
    } finally {
      _isChanging = false;
    }
  }

  Future<void> leave({bool updateState = true}) async {
    if (_isDisposed && updateState) return;
    if (!_isFullscreen) return;
    try {
      if (Platform.isWindows) {
        await _leaveWindowsFullscreen(updateState: updateState);
      } else {
        await windowManager.setFullScreen(false);
      }
    } catch (_) {}
    _isFullscreen = false;
  }

  void dispose() {
    if (_isDisposed) return;
    _isDisposed = true;
    if (_isFullscreen) {
      _scheduleDisposeRestore();
    }
  }

  @visibleForTesting
  Future<void>? get disposeRestoreFuture => _disposeRestoreFuture;

  void _scheduleDisposeRestore() {
    if (_disposeRestoreFuture != null) return;
    final restore = _restoreAfterDispose();
    _disposeRestoreFuture = restore;
    unawaited(restore);
  }

  Future<void> _restoreAfterDispose() async {
    while (_isChanging) {
      await Future<void>.delayed(Duration.zero);
    }
    if (!_isFullscreen) return;

    _isChanging = true;
    try {
      if (Platform.isWindows) {
        await _leaveWindowsFullscreen(updateState: false);
      } else {
        await windowManager.setFullScreen(false);
        _isFullscreen = false;
      }
    } catch (error) {
      AppLogger.warn(_tag, 'Failed to restore window on dispose', error);
    } finally {
      _isChanging = false;
    }
  }

  void _setFullscreenState(bool fullscreen) {
    if (_isFullscreen == fullscreen) return;
    _isFullscreen = fullscreen;
    if (!_isDisposed) {
      _onStateChanged();
    }
  }

  Future<bool?> _readNativeFullscreenState() async {
    try {
      return await windowManager.isFullScreen();
    } catch (error) {
      AppLogger.warn(_tag, 'Failed to read state', error);
      return null;
    }
  }

  Future<void> _toggleNativeFullscreen() async {
    final current = await _readNativeFullscreenState() ?? _isFullscreen;
    final next = !current;
    await windowManager.setFullScreen(next);
    final actual = await _readNativeFullscreenState() ?? next;
    _setFullscreenState(actual);
    if (_isDisposed) {
      _scheduleDisposeRestore();
    }
  }

  Future<void> _enterWindowsFullscreen() async {
    final currentBounds = await windowManager.getBounds();
    final display = await _displayForBounds(currentBounds);

    _windowBoundsBeforeFullscreen = currentBounds;
    _wasMaximized = await windowManager.isMaximized();
    _wasResizable = await windowManager.isResizable();
    _wasAlwaysOnTop = await windowManager.isAlwaysOnTop();

    if (_wasMaximized) {
      await windowManager.unmaximize();
    }
    await windowManager.setResizable(false);
    await windowManager.setAlwaysOnTop(true);
    await windowManager.setTitleBarStyle(
      TitleBarStyle.hidden,
      windowButtonVisibility: false,
    );
    await windowManager.setBounds(fullscreenBoundsForDisplay(display));
    await windowManager.focus();
    _setFullscreenState(true);
    if (_isDisposed) {
      _scheduleDisposeRestore();
    }
  }

  Future<void> _leaveWindowsFullscreen({bool updateState = true}) async {
    await windowManager.setTitleBarStyle(
      TitleBarStyle.hidden,
      windowButtonVisibility: false,
    );
    await windowManager.setResizable(_wasResizable);
    await windowManager.setAlwaysOnTop(_wasAlwaysOnTop);

    if (_wasMaximized) {
      await windowManager.maximize();
    } else {
      final bounds = _windowBoundsBeforeFullscreen;
      if (bounds != null) {
        await windowManager.setBounds(bounds);
      }
    }
    await windowManager.focus();

    if (updateState) {
      _setFullscreenState(false);
    } else {
      _isFullscreen = false;
    }
  }

  Future<Display> _displayForBounds(Rect bounds) async {
    final displays = await screenRetriever.getAllDisplays();
    final center = bounds.center;
    for (final display in displays) {
      final rect = fullscreenBoundsForDisplay(display);
      if (rect.contains(center)) return display;
    }
    if (displays.isNotEmpty) return displays.first;
    return screenRetriever.getPrimaryDisplay();
  }

  @visibleForTesting
  static Rect fullscreenBoundsForDisplay(Display display) {
    final position = display.visiblePosition ?? Offset.zero;
    final size = display.size;
    if (Platform.isWindows) {
      const border = 8.0;
      return Rect.fromLTWH(
        position.dx - border,
        position.dy,
        size.width + border * 2,
        size.height + border,
      );
    }
    return position & size;
  }
}
