// lib/widgets/cast/google_cast_controller.dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;

import 'package:aves/model/entry/entry.dart';
import 'package:aves/services/cast/cast_server.dart';
import 'package:aves/remote/remote_settings.dart';
import 'package:aves/widgets/cast/cast_controller.dart';

class GoogleCastController {
  GoogleCastController._() {
    _channel.setMethodCallHandler(_onNativeEvent);

    // Timer leggero: aggiorna progress bar ogni 1 secondo
    Timer.periodic(const Duration(seconds: 1), (_) {
      if (isPlayingNotifier.value) {
        final pos = positionNotifier.value + const Duration(seconds: 1);
        if (pos < durationNotifier.value) {
          positionNotifier.value = pos;
          positionNotifier.notifyListeners();
        }
      }
    });
  }

  static final GoogleCastController instance = GoogleCastController._();
  static const MethodChannel _channel = MethodChannel('google_cast');

  // ⭐ Attiva Google Cast come proprietario del channel
  void activateMethodChannelHandler() {
    _channel.setMethodCallHandler(_onNativeEvent);
  }

  // ⭐ Disattiva Google Cast quando passi al backend custom
  void deactivateMethodChannelHandler() {
  }

  // UI state
  final ValueNotifier<CastIconState> castIconState =
      ValueNotifier(CastIconState.idle);

  final ValueNotifier<bool> isCastingNotifier = ValueNotifier(false);
  final ValueNotifier<int> currentIndexNotifier = ValueNotifier(0);

  final ValueNotifier<bool> isPlayingNotifier = ValueNotifier(false);
  final ValueNotifier<bool> isBufferingNotifier = ValueNotifier(false);

  final ValueNotifier<Duration> positionNotifier =
      ValueNotifier(Duration.zero);
  final ValueNotifier<Duration> durationNotifier =
      ValueNotifier(Duration.zero);

  final ValueNotifier<double> volumeNotifier = ValueNotifier(1.0);
  final ValueNotifier<bool> isMutedNotifier = ValueNotifier(false);

  // slideshow
  Timer? _slideshowTimer;
  bool _slideshowActive = false;
  int _slideshowIntervalSeconds = 5;

  // remote share cache
  final Map<String, String> _shareCache = {};
  String? _cachedToken;
  DateTime? _tokenExpiry;

  List<String>? resolvedUrls;
  List<bool>? resolvedIsVideo;

  int _currentIndex = 0;
  bool _currentIsVideo = false;

  Completer<void> _castReadyCompleter = Completer<void>();

  // ------------------------------------------------------------
  // RESET TOTALE (usato dal selettore backend)
  // ------------------------------------------------------------
  void forceReset() {
    stopSlideshow();
    resolvedUrls = null;
    resolvedIsVideo = null;
    _currentIsVideo = false;
    _currentIndex = 0;

    castIconState.value = CastIconState.idle;
    isCastingNotifier.value = false;

    _castReadyCompleter = Completer<void>();
    _resetPlaybackState();
  }

  // ------------------------------------------------------------
  // TOKEN
  // ------------------------------------------------------------
  Future<String> _getToken(RemoteSettings settings) async {
    if (_cachedToken != null &&
        _tokenExpiry != null &&
        DateTime.now().isBefore(_tokenExpiry!)) {
      return _cachedToken!;
    }

    final res = await http.post(
      Uri.parse("${settings.baseUrl}/auth/login"),
      headers: {"Content-Type": "application/json"},
      body: jsonEncode({
        "email": settings.email,
        "password": settings.password,
      }),
    );

    if (res.statusCode != 200) {
      throw Exception("Login remoto fallito");
    }

    final data = jsonDecode(res.body);
    _cachedToken = data["token"];
    _tokenExpiry = DateTime.now().add(const Duration(minutes: 10));
    return _cachedToken!;
  }

  Future<String> _resolveCastUrl(AvesEntry e) async {
    if ((e.origin ?? 0) == 0) {
      return castServer.buildUrlForEntry(e);
    }

    final remoteId = e.remoteId!;
    if (_shareCache.containsKey(remoteId)) {
      return _shareCache[remoteId]!;
    }

    final settings = await RemoteSettings.load();
    final token = await _getToken(settings);

    final res = await http.post(
      Uri.parse("${settings.baseUrl}/share/create"),
      headers: {
        "Authorization": "Bearer $token",
        "Content-Type": "application/json",
      },
      body: jsonEncode({
        "entryId": remoteId,
        "ttlSeconds": 600,
      }),
    );

    final data = jsonDecode(res.body);
    final url = (data["url"] as String).replaceFirst("http://", "https://");

    _shareCache[remoteId] = url;
    return url;
  }

  // ------------------------------------------------------------
  // EVENTI NATIVI
  // ------------------------------------------------------------
  Future<void> _onNativeEvent(MethodCall call) async {
    switch (call.method) {
      case "onCastReady":
        castIconState.value = CastIconState.connected;
        isCastingNotifier.value = true;

        if (!_castReadyCompleter.isCompleted) {
          _castReadyCompleter.complete();
        }
        break;

      case "onCastStopped":
        castIconState.value = CastIconState.idle;
        isCastingNotifier.value = false;

        resolvedUrls = null;
        resolvedIsVideo = null;
        _currentIsVideo = false;
        _castReadyCompleter = Completer<void>();

        stopSlideshow();
        _resetPlaybackState();
        break;

      case "onQueueIndexChanged":
        final args = (call.arguments as Map?) ?? {};
        final index = args["index"] ?? 0;
        final isVideo = args["isVideo"] == true;

        _currentIndex = index;
        currentIndexNotifier.value = index;

        _currentIsVideo = isVideo;

        if (_slideshowActive && !isVideo) {
          _scheduleNextPhoto();
        } else if (_slideshowActive && isVideo) {
          _slideshowTimer?.cancel();
        }
        break;

      case "onVideoFinished":
        if (_slideshowActive) {
          await _channel.invokeMethod('next');
        }
        break;

      case "onMediaStatus":
        final data = (call.arguments as Map?) ?? {};

        isPlayingNotifier.value = data["isPlaying"] == true;
        isBufferingNotifier.value = data["isBuffering"] == true;

        final pos = (data["position"] as num?)?.toInt() ?? 0;
        final dur = (data["duration"] as num?)?.toInt() ?? 0;

        positionNotifier.value = Duration(milliseconds: pos);
        durationNotifier.value = Duration(milliseconds: dur);

        positionNotifier.notifyListeners();
        durationNotifier.notifyListeners();
        break;

      case "onVolumeChanged":
        final data = (call.arguments as Map?) ?? {};

        volumeNotifier.value =
            ((data["volume"] as num?)?.toDouble() ?? 1.0)
                .clamp(0.0, 1.0);

        isMutedNotifier.value = data["isMuted"] == true;

        volumeNotifier.notifyListeners();
        isMutedNotifier.notifyListeners();
        break;
    }
  }

  void _resetPlaybackState() {
    isPlayingNotifier.value = false;
    isBufferingNotifier.value = false;
    positionNotifier.value = Duration.zero;
    durationNotifier.value = Duration.zero;
  }

  Future<void> _waitReady() async {
    if (_castReadyCompleter.isCompleted) return;
    await _castReadyCompleter.future;
  }

  // ------------------------------------------------------------
  // QUEUE
  // ------------------------------------------------------------
  Future<List<Map<String, dynamic>>> _buildQueue(
      List<AvesEntry> entries) async {

    // ⭐⭐⭐ PATCH FONDAMENTALE ⭐⭐⭐
    // Avvia il server Google Cast se non è già attivo
    await castServer.ensureStarted();

    final resolved = await Future.wait(entries.map((e) async {
      final url = await _resolveCastUrl(e);
      final isVideo = (e.durationMillis ?? 0) > 0;
      return {
        "url": url,
        "isVideo": isVideo,
        "mimeType": e.mimeType,
        "title": e.bestTitle ?? "",
      };
    }));

    resolvedUrls = resolved.map((it) => it["url"] as String).toList();
    resolvedIsVideo = resolved.map((it) => it["isVideo"] as bool).toList();

    return resolved;
  }

  Future<void> startManualQueue({
    required List<AvesEntry> entries,
    required int startIndex,
  }) async {
    currentIndexNotifier.value = startIndex;
    _currentIndex = startIndex;

    await _waitReady();

    final items = await _buildQueue(entries);

    _currentIsVideo = resolvedIsVideo?[startIndex] ?? false;

    await _channel.invokeMethod('loadQueue', {
      "items": items,
      "startIndex": startIndex,
    });
  }

  // ------------------------------------------------------------
  // SLIDESHOW
  // ------------------------------------------------------------
  Future<void> startSlideshow({
    required List<AvesEntry> entries,
    required int startIndex,
    int intervalSeconds = 5,
  }) async {
    stopSlideshow();

    _slideshowActive = true;
    _slideshowIntervalSeconds = intervalSeconds;

    await _waitReady();

    await startManualQueue(entries: entries, startIndex: startIndex);
  }

  void _scheduleNextPhoto() {
    _slideshowTimer?.cancel();

    if (_currentIsVideo) return;

    _slideshowTimer = Timer(
      Duration(seconds: _slideshowIntervalSeconds),
      () async {
        if (!_slideshowActive) return;
        await _channel.invokeMethod('next');
      },
    );
  }

  void stopSlideshow() {
    _slideshowTimer?.cancel();
    _slideshowTimer = null;
    _slideshowActive = false;
  }

  // ------------------------------------------------------------
  // CONTROLLI
  // ------------------------------------------------------------
  Future<void> playCast() => _channel.invokeMethod('play');

  Future<void> pauseCast() => _channel.invokeMethod('pause');

  Future<void> seekCast(Duration position) async {
    final ms = position.inMilliseconds;

    if ((positionNotifier.value.inMilliseconds - ms).abs() < 500) return;

    await _channel.invokeMethod('seekTo', {
      "position": ms,
    });

    positionNotifier.value = position;
    positionNotifier.notifyListeners();
  }

  Future<void> setCastVolume(double value) async {
    final clamped = value.clamp(0.0, 1.0);

    volumeNotifier.value = clamped;
    volumeNotifier.notifyListeners();

    await _channel.invokeMethod('setVolume', {
      "volume": clamped,
    });
  }

  Future<void> toggleMuteCast() async {
    final mute = !isMutedNotifier.value;

    await _channel.invokeMethod('setMute', {
      "mute": mute,
    });

    isMutedNotifier.value = mute;
    isMutedNotifier.notifyListeners();
  }

  Future<void> nextManual() => _channel.invokeMethod('next');
  Future<void> prevManual() => _channel.invokeMethod('prev');

  // ------------------------------------------------------------
  // STOP
  // ------------------------------------------------------------
  Future<void> stopGoogleCast() async {
    stopSlideshow();
    resolvedUrls = null;
    resolvedIsVideo = null;

    castIconState.value = CastIconState.idle;
    isCastingNotifier.value = false;

    _currentIsVideo = false;
    _castReadyCompleter = Completer<void>();
    _resetPlaybackState();

    return _channel.invokeMethod('stopCast');
  }

  // ------------------------------------------------------------
  // UI
  // ------------------------------------------------------------
  Future<void> showGoogleCastDialog() {
    CastController.instance.castIconState.value = CastIconState.connecting;
    return _channel.invokeMethod('showCastDialog');
  }


  // ------------------------------------------------------------
  // API pubbliche per FAST backend
  // ------------------------------------------------------------
  Future<List<Map<String, dynamic>>> buildQueue(List<AvesEntry> entries) {
    return _buildQueue(entries);
  }

  MethodChannel get channel => _channel;


}

