import 'dart:async';

import 'package:flutter/foundation.dart';

import '../models/player_status.dart';
import '../services/http_api_service.dart';
import '../services/web_socket_service.dart';
import 'debug_provider.dart';

class PlayerProvider extends ChangeNotifier {
  PlayerProvider({
    required HttpApiService httpApiService,
    required WebSocketService webSocketService,
    required DebugProvider debugProvider,
  })  : _httpApiService = httpApiService,
        _webSocketService = webSocketService,
        _debugProvider = debugProvider {
    _statusSubscription = _webSocketService.onStatusUpdate.listen((payload) {
      _status = PlayerStatus.fromJson(payload);
      _debugProvider.addWsLog('Player status update', details: payload);
      notifyListeners();
    });
    _stateSubscription = _webSocketService.onStateUpdate.listen((payload) {
      _currentState = payload['new_state']?.toString() ?? _currentState;
      _debugProvider.addWsLog('Player state update', details: payload);
      notifyListeners();
    });
    _configSubscription = _webSocketService.onConfigUpdate.listen((payload) {
      _updateSceneOptions(payload);
      _debugProvider.addWsLog('Player config update', details: payload);
      notifyListeners();
    });
  }

  final HttpApiService _httpApiService;
  final WebSocketService _webSocketService;
  final DebugProvider _debugProvider;
  late final StreamSubscription<Map<String, dynamic>> _statusSubscription;
  late final StreamSubscription<Map<String, dynamic>> _stateSubscription;
  late final StreamSubscription<Map<String, dynamic>> _configSubscription;

  PlayerStatus _status = PlayerStatus.initial();
  List<String> _playlist = const <String>[];
  List<String> _sceneOptions = const <String>['idle', 'intro', 'loop'];
  bool _isLoading = false;
  String? _errorMessage;
  String? _currentState;

  PlayerStatus get status => _status;
  List<String> get playlist => _playlist;
  List<String> get sceneOptions => _sceneOptions;
  bool get isLoading => _isLoading;
  String? get errorMessage => _errorMessage;
  String get currentState => _currentState ?? _status.currentVideo ?? 'idle';

  Future<void> bootstrap() async {
    _setLoading(true);
    _debugProvider.addHttpLog('Bootstrap player provider');
    try {
      final results = await Future.wait<dynamic>([
        _httpApiService.getPlaybackStatus(),
        _httpApiService.getPlaylist(),
        _httpApiService.getConfig(),
      ]);
      _status = results[0] as PlayerStatus;
      _playlist = results[1] as List<String>;
      _updateSceneOptions(results[2] as Map<String, dynamic>);
      _errorMessage = null;
      _debugProvider.addHttpLog(
        'Player provider bootstrapped',
        details: <String, Object>{'playlist': _playlist.length},
      );
    } catch (error) {
      _errorMessage = error.toString();
      _debugProvider.addHttpLog('Bootstrap player provider failed', details: error);
    } finally {
      _setLoading(false);
    }
  }

  Future<void> fetchStatus() async {
    _debugProvider.addHttpLog('Fetch playback status');
    try {
      _status = await _httpApiService.getPlaybackStatus();
      _errorMessage = null;
      _debugProvider.addHttpLog('Playback status fetched');
      notifyListeners();
    } catch (error) {
      _errorMessage = error.toString();
      _debugProvider.addHttpLog('Fetch playback status failed', details: error);
      notifyListeners();
    }
  }

  Future<void> fetchPlaylist() async {
    _debugProvider.addHttpLog('Fetch playlist');
    try {
      _playlist = await _httpApiService.getPlaylist();
      _errorMessage = null;
      _debugProvider.addHttpLog(
        'Playlist fetched',
        details: <String, Object>{'items': _playlist.length},
      );
      notifyListeners();
    } catch (error) {
      _errorMessage = error.toString();
      _debugProvider.addHttpLog('Fetch playlist failed', details: error);
      notifyListeners();
    }
  }

  Future<void> play() => _runCommand(_httpApiService.play);

  Future<void> pause() => _runCommand(_httpApiService.pause);

  Future<void> next() => _runCommand(_httpApiService.next);

  Future<void> previous() => _runCommand(_httpApiService.previous);

  Future<void> gotoIndex(int index) async {
    await _runCommand(() => _httpApiService.goto(index));
  }

  Future<void> switchScene(String name) async {
    await _runCommand(() => _httpApiService.changeScene(name));
    _currentState = name;
    notifyListeners();
  }

  Future<void> triggerEvent(String name, {String? value}) {
    return _runCommand(() => _httpApiService.trigger(name, value));
  }

  Future<void> togglePlayPause() async {
    if (_status.running && !_status.paused) {
      await pause();
      return;
    }
    await play();
  }

  Future<void> _runCommand(Future<dynamic> Function() action) async {
    _setLoading(true);
    _debugProvider.addHttpLog('Run player command');
    try {
      await action();
      await Future.wait<void>([
        fetchStatus(),
        fetchPlaylist(),
      ]);
      _errorMessage = null;
      _debugProvider.addHttpLog('Player command completed');
    } catch (error) {
      _errorMessage = error.toString();
      _debugProvider.addHttpLog('Player command failed', details: error);
      notifyListeners();
    } finally {
      _setLoading(false);
    }
  }

  void _updateSceneOptions(Map<String, dynamic> config) {
    final candidates = <String>{};
    final scenes = config['scenes'];
    if (scenes is List) {
      for (final scene in scenes) {
        final value = scene.toString();
        if (value.isNotEmpty) {
          candidates.add(value);
        }
      }
    }

    final stateMachine = config['state_machine'];
    if (stateMachine is Map) {
      final states = stateMachine['states'];
      if (states is Map) {
        for (final entry in states.keys) {
          final value = entry.toString();
          if (value.isNotEmpty) {
            candidates.add(value);
          }
        }
      }
      final initialState = stateMachine['initial_state']?.toString();
      if (initialState != null && initialState.isNotEmpty) {
        candidates.add(initialState);
        _currentState ??= initialState;
      }
    }

    if (candidates.isNotEmpty) {
      _sceneOptions = candidates.toList(growable: false)..sort();
    }
  }

  void _setLoading(bool value) {
    _isLoading = value;
    notifyListeners();
  }

  @override
  void dispose() {
    unawaited(_statusSubscription.cancel());
    unawaited(_stateSubscription.cancel());
    unawaited(_configSubscription.cancel());
    super.dispose();
  }
}
