// lib/widgets/cast/backends/dlna_cast_backend.dart
//
// Backend DLNA estratto dal vecchio CastController.
// Gestisce:
// - selezione renderer
// - avvio media server locale
// - cast di una entry
// - slideshow DLNA
// - next/prev manuali
// - heartbeat
// - stop DLNA

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

import 'package:flutter/material.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/services/cast/cast_server.dart';
import 'package:aves/widgets/cast/dlna.dart';

import '../cast_controller.dart';

class DlnaCastBackend extends CastBackendBase {
  DlnaCastBackend(super.controller);

  DLNADevice? _renderer;
  HttpServer? _mediaServer;

  Timer? _slideshowTimer;
  Timer? _heartbeatTimer;

  int _currentIndex = 0;
  List<AvesEntry>? _entries;

  // ------------------------------------------------------------
  // API: set renderer dall’esterno (CastIconButton)
  // ------------------------------------------------------------
  void setRenderer(DLNADevice device) {
    _renderer = device;
  }

  // ------------------------------------------------------------
  // FOTO/VIDEO → delay slideshow
  // ------------------------------------------------------------
  @override
  Duration? getNextDelay(AvesEntry entry) {
    final isVideo = entry.mimeType?.startsWith('video') ?? false;

    if (isVideo) {
      // 🎬 VIDEO → nessun timer → aspettiamo STOPPED
      return null;
    }

    // 📸 FOTO → timer 5 secondi
    return const Duration(seconds: 5);
  }

  // ------------------------------------------------------------
  // Prewarm media server
  // ------------------------------------------------------------
  Future<void> ensureMediaServerPrewarm() async {
    await _ensureMediaServer();
  }

  // ------------------------------------------------------------
  // START CAST (equivale a fullscreen DLNA)
  // ------------------------------------------------------------
  @override
  Future<void> startCast(List<AvesEntry> entries, int index) =>
      startFullscreen(entries, index);

  // ------------------------------------------------------------
  // FULLSCREEN DLNA
  // ------------------------------------------------------------
  @override
  Future<void> startFullscreen(List<AvesEntry> entries, int index) async {
    _entries = entries;
    _currentIndex = index;

    controller.castIconState.value = CastIconState.connecting;
    controller.isCastingNotifier.value = true;

    await _ensureRenderer();
    await _waitForDlnaReady();
    await _ensureMediaServer();

    await _castEntry(entries[index]);

    controller.castIconState.value = CastIconState.connected;
  }

  // ------------------------------------------------------------
  // SLIDESHOW DLNA (manuale)
  // ------------------------------------------------------------
  @override
  Future<void> startSlideshow(List<AvesEntry> entries, int index) async {
    await startFullscreen(entries, index);

    _slideshowTimer?.cancel();
    _slideshowTimer = Timer.periodic(
      const Duration(seconds: 5),
      (_) => nextManual(),
    );
  }

  // ------------------------------------------------------------
  // QUEUE MANUALE DLNA
  // ------------------------------------------------------------
  @override
  Future<void> startManualQueue(List<AvesEntry> entries, int index) async {
    _entries = entries;
    _currentIndex = index;

    controller.castIconState.value = CastIconState.connecting;
    controller.isCastingNotifier.value = true;

    await _ensureRenderer();
    await _waitForDlnaReady();
    await _ensureMediaServer();

    await _castEntry(entries[index]);

    controller.castIconState.value = CastIconState.connected;
  }

  // ------------------------------------------------------------
  // NEXT / PREV
  // ------------------------------------------------------------
  @override
  Future<void> nextManual() async {
    if (_entries == null) return;

    _currentIndex++;
    if (_currentIndex >= _entries!.length) _currentIndex = 0;

    await _castEntry(_entries![_currentIndex]);
  }

  @override
  Future<void> prevManual() async {
    if (_entries == null) return;

    _currentIndex--;
    if (_currentIndex < 0) _currentIndex = _entries!.length - 1;

    await _castEntry(_entries![_currentIndex]);
  }

  // ------------------------------------------------------------
  // STOP DLNA
  // ------------------------------------------------------------
  @override
  Future<void> stopCast() async {
    _slideshowTimer?.cancel();
    _slideshowTimer = null;

    _heartbeatTimer?.cancel();
    _heartbeatTimer = null;

    try {
      await _renderer?.stop();
    } catch (_) {}

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

  // ------------------------------------------------------------
  // CAST ENTRY DLNA
  // ------------------------------------------------------------
  Future<void> _castEntry(AvesEntry entry) async {
    debugPrint('DLNA: castEntry() renderer=$_renderer entry=${entry.id}');

    if (_renderer == null) {
      debugPrint('DLNA: _castEntry chiamato senza renderer');
      return;
    }

    await castServer.ensureStarted();

    // URL corretto per DLNA: usa il media server DLNA locale
    final url =
        'http://${_mediaServer!.address.host}:${_mediaServer!.port}/${entry.id}';
    debugPrint('DLNA: URL generato = $url');
    debugPrint('DLNA: serving entry ${entry.id} at $url');

    final bool isVideo = (entry.durationMillis ?? 0) > 0;

    try {
      await _renderer!.setUrl(
        url,
        title: entry.bestTitle ?? '',
        type: isVideo ? PlayType.Video : PlayType.Image,
      );
    } catch (e) {
      // Fallback Sony Bravia
      if (_isSonyBravia) {
        debugPrint('DLNA: fallback Sony Bravia, ritento senza metadata');
        await _renderer!.setUrl(
          url,
          title: '',
          type: PlayType.Image,
        );
      } else {
        debugPrint('DLNA: errore setUrl → $e');
        return;
      }
    }

    try {
      await _renderer!.play();
    } catch (e) {
      debugPrint('DLNA: play non supportato → $e');
    }

    _startHeartbeat();
  }

  // ------------------------------------------------------------
  // HEARTBEAT DLNA
  // ------------------------------------------------------------
  void _startHeartbeat() {
    _heartbeatTimer?.cancel();
    _heartbeatTimer = Timer.periodic(
      const Duration(seconds: 5),
      (_) async {
        try {
          await DLNAHttp.postSoap(
            url: _renderer!.avTransportUrl,
            serviceType: 'urn:schemas-upnp-org:service:AVTransport:1',
            action: 'GetTransportInfo',
            body: '''
<u:GetTransportInfo xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
  <InstanceID>0</InstanceID>
</u:GetTransportInfo>
''',
          );
        } catch (_) {}
      },
    );
  }

  // ------------------------------------------------------------
  // MEDIA SERVER LOCALE
  // ------------------------------------------------------------
  Future<void> _ensureMediaServer() async {
    if (_mediaServer != null) return;

    final interfaces = await NetworkInterface.list(
      includeLoopback: false,
      type: InternetAddressType.IPv4,
    );

    String? ip;
    for (final iface in interfaces) {
      for (final addr in iface.addresses) {
        if (!addr.isLoopback && addr.address.startsWith('192.168.')) {
          ip = addr.address;
          break;
        }
      }
      if (ip != null) break;
    }

    if (ip == null) return;

    _mediaServer = await HttpServer.bind(ip, 0, shared: true);

    _mediaServer!.listen((req) async {
      final id = int.tryParse(req.uri.path.substring(1));
      if (id == null) {
        req.response.statusCode = 404;
        return req.response.close();
      }

      final entry = _entries?.firstWhere((e) => e.id == id);
      if (entry == null) {
        req.response.statusCode = 404;
        return req.response.close();
      }

      final file = File(entry.path!);
      if (!await file.exists()) {
        req.response.statusCode = 404;
        return req.response.close();
      }

      final length = await file.length();
      final range = req.headers.value('range');

      if (range == null) {
        req.response.headers.set('Content-Type', entry.mimeType);
        req.response.headers.set('Content-Length', '$length');
        req.response.headers.set('Accept-Ranges', 'bytes');
        await req.response.addStream(file.openRead());
        return req.response.close();
      }

      final spec = range.substring('bytes='.length);
      final parts = spec.split('-');
      int start = int.tryParse(parts[0]) ?? 0;
      int end =
          parts.length > 1 ? int.tryParse(parts[1]) ?? length - 1 : length - 1;

      start = start.clamp(0, length - 1);
      end = end.clamp(start, length - 1);

      req.response.statusCode = 206;
      req.response.headers.set('Content-Type', entry.mimeType);
      req.response.headers.set('Content-Length', '${end - start + 1}');
      req.response.headers
          .set('Content-Range', 'bytes $start-$end/$length');
      req.response.headers.set('Accept-Ranges', 'bytes');

      await req.response.addStream(file.openRead(start, end + 1));
      return req.response.close();
    });
  }

  // ------------------------------------------------------------
  // RENDERER READY
  // ------------------------------------------------------------
  Future<void> _waitForDlnaReady() async {
    if (_renderer == null) return;

    for (int i = 0; i < 20; i++) {
      try {
        final resp = await DLNAHttp.postSoap(
          url: _renderer!.avTransportUrl,
          serviceType: 'urn:schemas-upnp-org:service:AVTransport:1',
          action: 'GetTransportInfo',
          body: '''
<u:GetTransportInfo xmlns:u="urn:schemas-upnp-org:service:AVTransport:1">
  <InstanceID>0</InstanceID>
</u:GetTransportInfo>
''',
        );

        final body = resp.body;

        if (body.contains('STOPPED') ||
            body.contains('NO_MEDIA_PRESENT')) {
          return;
        }
      } catch (_) {}

      await Future.delayed(const Duration(milliseconds: 300));
    }
  }

  // ------------------------------------------------------------
  // HELPERS
  // ------------------------------------------------------------
  bool get _isSonyBravia {
    final name = _renderer?.info.friendlyName.toLowerCase() ?? '';
    return name.contains('bravia') || name.contains('sony');
  }

  // ------------------------------------------------------------
  // SELEZIONE RENDERER DLNA
  // ------------------------------------------------------------
  Future<void> _ensureRenderer() async {
    if (_renderer != null) return;
    debugPrint('DLNA: _ensureRenderer chiamato senza renderer (setRenderer non ancora usato)');
  }
}
