import 'dart:io';
import 'dart:convert';  // For utf8
import 'package:path/path.dart' as p;
import '../interfaces/i_window_manager.dart';
import '../logging/logger.dart';
import '../config/config_manager.dart';

/// Manages cursor-positioned XP overlays by coordinating cursor tracking
/// and spawning Flutter overlay processes
class CursorOverlayManager {
  final IWindowManager _windowManager;
  final String _overlayAppPath;
  final bool _enabled;

  /// Create a CursorOverlayManager with the specified window manager and overlay app path
  /// 
  /// If overlayAppPath is not provided, tries to resolve the path automatically
  CursorOverlayManager({
    required IWindowManager windowManager,
    String? overlayAppPath,
    bool enabled = true,
  })  : _windowManager = windowManager,
        _overlayAppPath = overlayAppPath ?? _resolveOverlayAppPath(),
        _enabled = enabled;
        
  /// Attempts to find the overlay app binary path
  static String _resolveOverlayAppPath() {
    final List<String?> pathOptions = [
      // Check for configuration in xp_config.json
      ConfigManager.instance.getOverlayAppPath(),
    ];
    
    final List<String> possiblePaths = [
      // Check relative to the current script directory
      p.join(p.dirname(Platform.script.toFilePath()), '..', '..', '..', 'xp_overlay_app', 'build', 'linux', 'x64', 'release', 'bundle', 'xp_overlay_app'),
      
      // Check relative to the current working directory
      p.join(Directory.current.path, '..', 'xp_overlay_app', 'build', 'linux', 'x64', 'release', 'bundle', 'xp_overlay_app'),
      
      // Fixed paths for development
      '/home/nate/source/non-work/xp_nix/xp_overlay_app/build/linux/x64/release/bundle/xp_overlay_app',
    ];
    
    // Add non-null paths from options to possible paths
    for (final path in pathOptions) {
      if (path != null && path.isNotEmpty) {
        possiblePaths.add(path);
      }
    }
    
    for (final path in possiblePaths) {
      if (path != null && path.isNotEmpty && File(path).existsSync()) {
        Logger.info('Found overlay app at: $path');
        return path;
      }
    }
    
    Logger.warn('Could not find overlay app binary, overlays will be disabled');
    return '';
  }

  /// Show XP number at current cursor position
  /// 
  /// [xpValue] - XP amount to display
  /// [duration] - How long to show the overlay (default from config, fallback 3 seconds)
  Future<void> showXPNumber(
    int xpValue, {
    Duration? duration,
  }) async {
    // Skip if disabled
    if (!_enabled || !ConfigManager.instance.isOverlayEnabled()) {
      return;
    }
    
    // Skip if overlay app path is empty or doesn't exist
    if (_overlayAppPath.isEmpty || !await File(_overlayAppPath).exists()) {
      Logger.debug('Overlay app not found at: $_overlayAppPath, skipping XP overlay');
      return;
    }
    
    try {
      // Get current cursor position
      final cursorPos = await _windowManager.getCursorPosition();
      if (cursorPos == null) {
        Logger.warn('Could not get cursor position, skipping XP overlay');
        return;
      }

      final x = cursorPos['x']!;
      final y = cursorPos['y']!;

      // Use specified duration or get from config
      final overlayDuration = duration ?? 
          Duration(seconds: ConfigManager.instance.getOverlayDuration());

      Logger.info('Showing XP overlay: +$xpValue at position ($x, $y)');

      // Spawn Flutter overlay process
      await _spawnOverlayProcess(x, y, xpValue, overlayDuration);
    } catch (e) {
      Logger.error('Failed to show XP overlay', e);
    }
  }

  /// Show XP number at specific position
  /// 
  /// [x] - X coordinate in pixels
  /// [y] - Y coordinate in pixels  
  /// [xpValue] - XP amount to display
  /// [duration] - How long to show the overlay (default from config)
  Future<void> showXPNumberAt(
    int x, 
    int y, 
    int xpValue, 
    {Duration? duration}
  ) async {
    // Skip if disabled
    if (!_enabled || !ConfigManager.instance.isOverlayEnabled()) {
      return;
    }
    
    // Skip if overlay app path is empty or doesn't exist
    if (_overlayAppPath.isEmpty || !await File(_overlayAppPath).exists()) {
      Logger.debug('Overlay app not found at: $_overlayAppPath, skipping XP overlay');
      return;
    }
    
    try {
      // Use specified duration or get from config
      final overlayDuration = duration ?? 
          Duration(seconds: ConfigManager.instance.getOverlayDuration());
      
      Logger.info('Showing XP overlay: +$xpValue at position ($x, $y)');
      await _spawnOverlayProcess(x, y, xpValue, overlayDuration);
    } catch (e) {
      Logger.error('Failed to show XP overlay at position', e);
    }
  }

  /// Test cursor position tracking
  /// 
  /// Returns current cursor coordinates or null if unavailable
  Future<Map<String, int>?> getCurrentCursorPosition() async {
    try {
      return await _windowManager.getCursorPosition();
    } catch (e) {
      Logger.error('Failed to get cursor position', e);
      return null;
    }
  }

  /// Spawn the Flutter overlay process
  Future<void> _spawnOverlayProcess(int x, int y, int xpValue, Duration duration) async {
    final durationSeconds = duration.inSeconds;
    
    // Safety check - don't proceed if path is empty
    if (_overlayAppPath.isEmpty) {
      Logger.warn('Overlay app path is empty, cannot spawn process');
      return;
    }
    
    // Check if overlay app exists
    if (!await File(_overlayAppPath).exists()) {
      Logger.error('Overlay app not found at: $_overlayAppPath');
      return;
    }

    try {
      // Log the command we're about to run
      final commandArgs = [x.toString(), y.toString(), xpValue.toString(), durationSeconds.toString()];
      Logger.debug('Executing: $_overlayAppPath ${commandArgs.join(' ')}');
      
      // Spawn overlay process with arguments: x y xp_value duration_seconds
      final process = await Process.start(
        _overlayAppPath,
        commandArgs,
        mode: ProcessStartMode.detached,
      );

      Logger.debug('Spawned overlay process with PID: ${process.pid}');
      
      // Collect stderr output to log if there's an error
      process.stderr.transform(utf8.decoder).listen((data) {
        if (data.isNotEmpty) {
          Logger.warn('Overlay process stderr: $data');
        }
      });
      
      // Don't wait for the process to complete - let it run independently
      process.exitCode.then((exitCode) {
        if (exitCode != 0) {
          Logger.warn('Overlay process exited with code: $exitCode');
        } else {
          Logger.debug('Overlay process completed successfully');
        }
      }).catchError((error) {
        Logger.error('Overlay process error', error);
      });
      
    } catch (e) {
      Logger.error('Failed to spawn overlay process: $e');
    }
  }

  /// Test the overlay system by showing a test XP number
  /// 
  /// Performs additional validation and prints detailed diagnostic information
  Future<void> testOverlay({int testXP = 42}) async {
    Logger.info('Testing overlay system with XP value: $testXP');
    
    // Check if overlay is enabled in config
    final enabled = ConfigManager.instance.isOverlayEnabled();
    Logger.info('Overlay enabled in config: $enabled');
    
    // Check if overlay app exists
    final appExists = _overlayAppPath.isNotEmpty && await File(_overlayAppPath).exists();
    Logger.info('Overlay app path: $_overlayAppPath (exists: $appExists)');
    
    // Test cursor position detection
    final cursorPos = await getCurrentCursorPosition();
    if (cursorPos != null) {
      Logger.info('Cursor position detected at: (${cursorPos['x']}, ${cursorPos['y']})');
    } else {
      Logger.warn('Could not detect cursor position');
    }
    
    // Check window manager type
    Logger.info('Window manager type: ${_windowManager.managerType}');
    
    // Show test overlay if everything is ready
    if (enabled && appExists && cursorPos != null) {
      await showXPNumber(testXP, duration: const Duration(seconds: 5));
      Logger.info('Test overlay triggered successfully');
    } else {
      Logger.warn('Test overlay skipped due to validation failures');
    }
  }

  /// Check if the overlay system is available and ready to use
  Future<bool> isAvailable() async {
    try {
      // Check if overlay is enabled in config
      if (!_enabled || !ConfigManager.instance.isOverlayEnabled()) {
        Logger.debug('Overlay system disabled by configuration');
        return false;
      }
      
      // Check if overlay app path is valid
      if (_overlayAppPath.isEmpty) {
        Logger.debug('Overlay app path is empty');
        return false;
      }
      
      // Check if window manager supports cursor position
      final cursorPos = await _windowManager.getCursorPosition();
      final windowManagerAvailable = cursorPos != null;
      
      // Check if overlay app exists
      final overlayAppAvailable = await File(_overlayAppPath).exists();
      
      Logger.debug('Overlay system availability check: ' +
          'enabled=$_enabled, ' +
          'windowManager=$windowManagerAvailable, ' +
          'overlayAppExists=$overlayAppAvailable, ' +
          'overlayAppPath=$_overlayAppPath');
      
      return windowManagerAvailable && overlayAppAvailable;
    } catch (e) {
      Logger.error('Error checking overlay availability', e);
      return false;
    }
  }
}