import 'dart:async';
import 'dart:io' show Platform;

import 'package:flutter/material.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:permission_handler/permission_handler.dart';

import '../../core/app_colors.dart';
import '../../core/api_client.dart';
import '../../core/app_module_visuals.dart';
import '../../core/app_design_tokens.dart';
import '../../core/app_theme.dart';
import '../../core/navigation_provider.dart';
import '../../models/ble_device.dart';
import '../../models/bp_reading.dart';
import '../../providers/auth_provider.dart';
import '../../providers/omron_device_provider.dart';
import '../../services/health_ble_service.dart';
import '../../widgets/ble_sync_dialog.dart';
import '../../widgets/app_toast.dart';
import '../../widgets/app_empty_state.dart';
import 'device_sync_ui_logic.dart';

const _deviceVisual = AppModuleVisuals.device;

class DeviceManagementPage extends ConsumerStatefulWidget {
  const DeviceManagementPage({super.key});

  @override
  ConsumerState<DeviceManagementPage> createState() =>
      _DeviceManagementPageState();
}

class _DeviceManagementPageState extends ConsumerState<DeviceManagementPage>
    with SingleTickerProviderStateMixin {
  static const _freshScanWindow = Duration(seconds: 2);
  static const _sameDeviceRetryWindow = Duration(seconds: 3);

  StreamSubscription<List<ScanResult>>? _scanSub;
  late final HealthBleService _bleService;
  late final AnimationController _scanCtrl;
  late final Animation<double> _scanAnim;
  final _recentAttempts = <String, DateTime>{};
  String? _busyDeviceId;
  String? _connectedDeviceId;
  DateTime? _scanStartedAt;
  String _activeScanBoundKey = '';
  bool _scanning = false;
  bool _permissionBlocked = false;
  bool _disposed = false;
  String? _pageMessage;

  @override
  void initState() {
    super.initState();
    _bleService = ref.read(healthBleServiceProvider);
    _scanCtrl = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 1400),
    )..repeat(reverse: true);
    _scanAnim = Tween(
      begin: 0.85,
      end: 1.35,
    ).animate(CurvedAnimation(parent: _scanCtrl, curve: Curves.easeInOut));
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) unawaited(_startForegroundScan());
    });
  }

  @override
  void dispose() {
    _disposed = true;
    _scanSub?.cancel();
    unawaited(FlutterBluePlus.stopScan());
    unawaited(_bleService.disconnect());
    _scanCtrl.dispose();
    super.dispose();
  }

  Future<void> _startForegroundScan() async {
    if (!await _ensureBlePermissions()) {
      if (mounted) setState(() => _scanning = false);
      return;
    }
    if (_disposed || !mounted) return;

    if (Platform.isAndroid) {
      final locStatus = await Permission.locationWhenInUse.serviceStatus;
      if (_disposed || !mounted) return;
      if (locStatus != ServiceStatus.enabled && mounted) {
        AppToast.show(
          context,
          '请先打开定位服务，否则可能无法发现蓝牙设备',
          type: AppToastType.warning,
        );
      }
    }

    final adapterState = await FlutterBluePlus.adapterState.first;
    if (_disposed || !mounted) return;
    if (adapterState != BluetoothAdapterState.on) {
      try {
        await FlutterBluePlus.turnOn();
      } catch (_) {
        if (mounted) {
          setState(() => _pageMessage = '蓝牙未开启，无法查找已绑定设备');
          AppToast.show(
            context,
            deviceSyncErrorMessage(
              const DeviceSyncFailure(DeviceSyncFailureStage.bluetooth),
            ),
            type: AppToastType.warning,
          );
        }
        return;
      }
    }

    try {
      await FlutterBluePlus.stopScan();
    } catch (_) {}
    if (_disposed || !mounted) return;
    final boundIds = ref
        .read(omronDeviceProvider)
        .devices
        .where((device) => HealthBleService.isSyncImplemented(device.type))
        .map((device) => device.id)
        .toList();
    if (boundIds.isEmpty) {
      _activeScanBoundKey = '';
      if (mounted) setState(() => _scanning = false);
      return;
    }
    _activeScanBoundKey = _boundIdsKey(boundIds);
    _scanStartedAt = DateTime.now();
    await _scanSub?.cancel();
    _scanSub = FlutterBluePlus.onScanResults.listen(_onScanResults);
    try {
      await FlutterBluePlus.startScan(
        withRemoteIds: boundIds,
        androidScanMode: AndroidScanMode.lowLatency,
        oneByOne: true,
      );
      if (mounted) {
        setState(() {
          _scanning = true;
          _pageMessage = null;
        });
      }
    } catch (_) {
      if (mounted) {
        setState(() {
          _scanning = false;
          _pageMessage = '设备查找失败，请检查蓝牙状态后重试';
        });
        AppToast.show(context, _pageMessage!, type: AppToastType.error);
      }
    }
  }

  Future<bool> _ensureBlePermissions() async {
    Map<Permission, PermissionStatus> statuses;
    try {
      statuses = await [
        Permission.bluetoothScan,
        Permission.bluetoothConnect,
      ].request();
    } catch (_) {
      if (mounted) {
        setState(() => _pageMessage = '无法读取蓝牙权限状态');
        AppToast.show(context, _pageMessage!, type: AppToastType.error);
      }
      return false;
    }
    final granted = statuses.values.every((status) => status.isGranted);
    if (granted) {
      _permissionBlocked = false;
      return true;
    }
    _permissionBlocked = true;
    if (mounted) setState(() => _pageMessage = '需要蓝牙权限才能同步设备');
    if (_disposed || !mounted) return false;
    AppToast.show(context, '请开启蓝牙权限后再同步设备', type: AppToastType.warning);
    await showDialog<void>(
      context: context,
      builder: (ctx) => AlertDialog(
        title: const Text('需要蓝牙权限'),
        content: const Text('同步已绑定设备需要蓝牙权限，请在系统设置中开启后重试。'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('稍后再说'),
          ),
          TextButton(
            onPressed: () {
              Navigator.pop(ctx);
              openAppSettings();
            },
            child: const Text('去设置'),
          ),
        ],
      ),
    );
    return false;
  }

  void _onScanResults(List<ScanResult> results) {
    if (!mounted || _busyDeviceId != null) return;
    final scanStartedAt = _scanStartedAt;
    if (scanStartedAt == null) return;
    final boundDevices = ref.read(omronDeviceProvider).devices;
    if (boundDevices.isEmpty) return;

    final candidates = results.where((result) {
      final id = result.device.remoteId.toString();
      if (result.timeStamp.isBefore(scanStartedAt)) return false;
      if (!result.advertisementData.connectable) return false;
      if (DateTime.now().difference(result.timeStamp) > _freshScanWindow) {
        return false;
      }
      final boundDevice = ref.read(omronDeviceProvider).findById(id);
      if (boundDevice == null) return false;
      if (!_isSameBoundDeviceName(result, boundDevice)) return false;
      final scanType = HealthBleService.deviceTypeFromScan(result);
      if (scanType != null && scanType != boundDevice.type) return false;
      final lastAttempt = _recentAttempts[id];
      if (lastAttempt == null) return true;
      return DateTime.now().difference(lastAttempt) > _sameDeviceRetryWindow;
    }).toList()..sort((a, b) => b.rssi.compareTo(a.rssi));

    if (candidates.isEmpty) return;
    final result = candidates.first;
    final bound = ref
        .read(omronDeviceProvider)
        .findById(result.device.remoteId.toString());
    if (bound == null) return;
    unawaited(_syncDevice(result, bound));
  }

  bool _isSameBoundDeviceName(ScanResult result, BoundBleDevice bound) {
    final scannedName = _scanDeviceName(result);
    if (scannedName.isEmpty) return false;
    return _normalizeDeviceName(scannedName) ==
        _normalizeDeviceName(bound.name);
  }

  String _scanDeviceName(ScanResult result) {
    final advName = result.advertisementData.advName.trim();
    if (advName.isNotEmpty) return advName;
    return result.device.platformName.trim();
  }

  String _normalizeDeviceName(String value) {
    return value.trim().toUpperCase();
  }

  String _boundIdsKey(List<String> ids) {
    final sorted = [...ids]..sort();
    return sorted.join('|');
  }

  Future<void> _syncDevice(ScanResult result, BoundBleDevice bound) async {
    if (_disposed || !mounted || _busyDeviceId != null) return;
    _recentAttempts[bound.id] = DateTime.now();
    setState(() {
      _busyDeviceId = bound.id;
      _connectedDeviceId = null;
      _pageMessage = '正在连接 ${bound.name}';
    });

    await FlutterBluePlus.stopScan();
    if (_disposed || !mounted) return;
    setState(() => _scanning = false);

    try {
      final syncResult = await _bleService.syncBoundDevice(
        result.device,
        bound,
        timeout: const Duration(seconds: 35),
        onConnected: () {
          if (!_disposed && mounted) {
            setState(() {
              _connectedDeviceId = bound.id;
              _pageMessage = '已连接，等待设备测量数据';
            });
          }
        },
        onMeasurementReceived: () {
          // Measurement has arrived; parsing and persistence happen below.
        },
      );
      if (syncResult is BloodPressureSyncResult) {
        if (_disposed || !mounted) return;
        setState(() => _pageMessage = '已读取数据，正在安全上传');
        final saved = await _persistBloodPressure(
          syncResult.device,
          syncResult.reading,
        );
        if (!_disposed && mounted && saved) {
          setState(() => _pageMessage = '同步完成');
          await _showBloodPressureDialog(syncResult.reading);
        }
      }
    } on TimeoutException {
      if (!_disposed && mounted) {
        final message = _connectedDeviceId == bound.id
            ? deviceSyncErrorMessage(
                const DeviceSyncFailure(DeviceSyncFailureStage.measurement),
              )
            : deviceSyncErrorMessage(
                const DeviceSyncFailure(DeviceSyncFailureStage.connection),
              );
        setState(() => _pageMessage = message);
        AppToast.show(context, message, type: AppToastType.warning);
      }
    } on UnsupportedBleDeviceException catch (error) {
      if (!_disposed && mounted) {
        setState(() => _pageMessage = error.message);
        AppToast.show(context, error.message, type: AppToastType.warning);
      }
    } on ApiException catch (error) {
      if (!_disposed && mounted) {
        final message = deviceSyncErrorMessage(
          DeviceSyncFailure(DeviceSyncFailureStage.upload, error.message),
        );
        setState(() => _pageMessage = message);
        AppToast.show(context, message, type: AppToastType.error);
      }
    } on Exception catch (error) {
      if (!_disposed && mounted) {
        final message = deviceSyncErrorMessage(
          DeviceSyncFailure(DeviceSyncFailureStage.connection, '$error'),
        );
        setState(() => _pageMessage = message);
        AppToast.show(context, message, type: AppToastType.error);
      }
    } finally {
      await _bleService.disconnect();
      if (!_disposed && mounted) {
        setState(() {
          _busyDeviceId = null;
          _connectedDeviceId = null;
        });
      }
      if (!_disposed && mounted) unawaited(_startForegroundScan());
    }
  }

  Future<bool> _persistBloodPressure(
    BoundBleDevice device,
    BpReading reading,
  ) async {
    final notifier = ref.read(omronDeviceProvider.notifier);
    if (await notifier.isDuplicateReading(device, reading)) return false;

    final api = ref.read(apiClientProvider);
    final records = <Map<String, dynamic>>[reading.toHealthRecord()];
    final heartRateRecord = reading.toHeartRateRecord();
    if (heartRateRecord != null) {
      records.add(heartRateRecord);
    }
    await api.post('/api/health-records/batch', data: records);
    await notifier.recordSuccessfulSync(device: device, reading: reading);
    return true;
  }

  Future<void> _showBloodPressureDialog(BpReading reading) {
    return showBleSyncDialog(context, reading: reading);
  }

  @override
  Widget build(BuildContext context) {
    final state = ref.watch(omronDeviceProvider);
    final devices = state.devices;
    final syncableDevices = devices
        .where((device) => HealthBleService.isSyncImplemented(device.type))
        .toList();
    final boundKey = _boundIdsKey(
      syncableDevices.map((device) => device.id).toList(),
    );

    if (syncableDevices.isNotEmpty &&
        !_scanning &&
        _busyDeviceId == null &&
        !_permissionBlocked &&
        _activeScanBoundKey != boundKey) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (!_disposed && mounted) unawaited(_startForegroundScan());
      });
    }

    ref.listen<DeviceBindState>(omronDeviceProvider, (prev, next) {
      final nextIds = next.devices
          .where((device) => HealthBleService.isSyncImplemented(device.type))
          .map((device) => device.id)
          .toList();
      final nextKey = _boundIdsKey(nextIds);
      if (nextIds.isEmpty) {
        _activeScanBoundKey = '';
        unawaited(FlutterBluePlus.stopScan());
        if (!_disposed && mounted) setState(() => _scanning = false);
        return;
      }
      if (nextKey != _activeScanBoundKey && _busyDeviceId == null) {
        if (_permissionBlocked) return;
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (!_disposed && mounted) unawaited(_startForegroundScan());
        });
      }
    });

    return GradientScaffold(
      appBar: AppBar(
        title: const Text('蓝牙设备'),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => popRoute(ref),
        ),
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 10),
            child: IconButton(
              tooltip: '新增设备',
              onPressed: () => pushRoute(ref, 'deviceScan'),
              style: IconButton.styleFrom(
                backgroundColor: Colors.white,
                foregroundColor: AppColors.device,
                fixedSize: const Size(40, 40),
                side: const BorderSide(color: AppColors.deviceBorder),
                shape: const CircleBorder(),
              ),
              icon: const Icon(Icons.add_rounded),
            ),
          ),
        ],
      ),
      body: Stack(
        children: [
          ListView(
            padding: const EdgeInsets.fromLTRB(18, 10, 18, 120),
            children: [
              _DevicesHeader(deviceCount: devices.length),
              if (devices.isNotEmpty) ...[
                const SizedBox(height: 12),
                _SyncStatusBar(
                  message:
                      _pageMessage ??
                      (syncableDevices.isEmpty
                          ? '当前设备已绑定，暂不支持自动同步'
                          : _scanning
                          ? '正在等待已绑定设备的测量数据'
                          : '设备同步已暂停'),
                  active: _scanning || _busyDeviceId != null,
                  onRetry: _busyDeviceId == null ? _startForegroundScan : null,
                ),
              ],
              const SizedBox(height: 18),
              if (devices.isEmpty)
                const _EmptyDevices()
              else
                for (final type in BleDeviceType.values)
                  if (state.devicesOfType(type).isNotEmpty) ...[
                    _DeviceSection(
                      type: type,
                      devices: state.devicesOfType(type),
                      connectedDeviceId: _connectedDeviceId,
                      busyDeviceId: _busyDeviceId,
                      onUnbind: _confirmUnbind,
                    ),
                    const SizedBox(height: 18),
                  ],
            ],
          ),
          if (devices.isNotEmpty)
            Positioned(
              right: 18,
              bottom: 20,
              child: _ScanIndicator(
                animation: _scanAnim,
                scanning: _scanning,
                syncing: _connectedDeviceId != null || _busyDeviceId != null,
              ),
            ),
        ],
      ),
    );
  }

  Future<void> _confirmUnbind(BoundBleDevice device) async {
    final ok = await showDialog<bool>(
      context: context,
      builder: (ctx) => AlertDialog(
        shape: RoundedRectangleBorder(borderRadius: AppRadius.xlBorder),
        title: const Text('解绑设备'),
        content: Text('确定解绑这台${device.type.label}吗？'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx, false),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(ctx, true),
            style: TextButton.styleFrom(foregroundColor: AppColors.errorText),
            child: const Text('解绑'),
          ),
        ],
      ),
    );
    if (ok == true) {
      await ref.read(omronDeviceProvider.notifier).unbind(device.id);
    }
  }
}

class _ScanIndicator extends StatelessWidget {
  final Animation<double> animation;
  final bool scanning;
  final bool syncing;

  const _ScanIndicator({
    required this.animation,
    required this.scanning,
    required this.syncing,
  });

  @override
  Widget build(BuildContext context) {
    final active = scanning || syncing;
    return SizedBox(
      width: 88,
      height: 88,
      child: Stack(
        alignment: Alignment.center,
        children: [
          if (active) ...[
            AnimatedBuilder(
              animation: animation,
              builder: (context, child) => Container(
                width: 78 * animation.value,
                height: 78 * animation.value,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: AppColors.device.withValues(alpha: 0.10),
                ),
              ),
            ),
            AnimatedBuilder(
              animation: animation,
              builder: (context, child) => Container(
                width: 64 * animation.value,
                height: 64 * animation.value,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: AppColors.device.withValues(alpha: 0.18),
                ),
              ),
            ),
          ],
          Container(
            width: 58,
            height: 58,
            decoration: BoxDecoration(
              color: AppColors.device,
              shape: BoxShape.circle,
              boxShadow: [
                BoxShadow(
                  color: AppColors.device.withValues(alpha: 0.22),
                  blurRadius: 16,
                  offset: const Offset(0, 7),
                ),
              ],
            ),
            child: Icon(
              syncing
                  ? Icons.bluetooth_connected_rounded
                  : Icons.bluetooth_searching_rounded,
              color: Colors.white,
              size: 28,
            ),
          ),
        ],
      ),
    );
  }
}

class _DevicesHeader extends StatelessWidget {
  final int deviceCount;

  const _DevicesHeader({required this.deviceCount});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.fromLTRB(18, 18, 18, 18),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: AppRadius.lgBorder,
      ),
      child: Row(
        children: [
          Container(
            width: 50,
            height: 50,
            decoration: BoxDecoration(
              color: AppColors.device,
              borderRadius: AppRadius.smBorder,
            ),
            child: Icon(_deviceVisual.icon, color: Colors.white, size: 27),
          ),
          const SizedBox(width: 14),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  '已绑定设备',
                  style: TextStyle(
                    fontSize: 19,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textPrimary,
                  ),
                ),
                const SizedBox(height: 4),
                Text(
                  '$deviceCount 台设备',
                  style: const TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w600,
                    color: AppColors.textSecondary,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _DeviceSection extends StatelessWidget {
  final BleDeviceType type;
  final List<BoundBleDevice> devices;
  final String? connectedDeviceId;
  final String? busyDeviceId;
  final ValueChanged<BoundBleDevice> onUnbind;

  const _DeviceSection({
    required this.type,
    required this.devices,
    required this.connectedDeviceId,
    required this.busyDeviceId,
    required this.onUnbind,
  });

  @override
  Widget build(BuildContext context) {
    return ClipRRect(
      borderRadius: AppRadius.lgBorder,
      child: ColoredBox(
        color: Colors.white,
        child: Column(
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(14, 13, 14, 10),
              child: Row(
                children: [
                  Container(
                    width: 34,
                    height: 34,
                    decoration: BoxDecoration(
                      color: AppColors.device,
                      borderRadius: AppRadius.smBorder,
                    ),
                    child: Icon(type.icon, size: 19, color: Colors.white),
                  ),
                  const SizedBox(width: 10),
                  Text(
                    type.label,
                    style: const TextStyle(
                      fontSize: 16,
                      fontWeight: FontWeight.w700,
                      color: AppColors.textPrimary,
                    ),
                  ),
                  const Spacer(),
                  Text(
                    '${devices.length} 台',
                    style: const TextStyle(
                      fontSize: 13,
                      fontWeight: FontWeight.w700,
                      color: AppColors.textHint,
                    ),
                  ),
                ],
              ),
            ),
            for (var index = 0; index < devices.length; index++)
              _BoundDeviceTile(
                device: devices[index],
                connected: connectedDeviceId == devices[index].id,
                busy: busyDeviceId == devices[index].id,
                showDivider: index < devices.length - 1,
                onUnbind: () => onUnbind(devices[index]),
              ),
          ],
        ),
      ),
    );
  }
}

class _BoundDeviceTile extends StatelessWidget {
  final BoundBleDevice device;
  final bool connected;
  final bool busy;
  final bool showDivider;
  final VoidCallback onUnbind;

  const _BoundDeviceTile({
    required this.device,
    required this.connected,
    required this.busy,
    required this.showDivider,
    required this.onUnbind,
  });

  @override
  Widget build(BuildContext context) {
    final availability = deviceSyncAvailabilityLabel(device.type);
    return AnimatedContainer(
      duration: const Duration(milliseconds: 180),
      padding: const EdgeInsets.only(left: 14),
      decoration: BoxDecoration(
        color: connected
            ? AppColors.successLight.withValues(alpha: 0.45)
            : Colors.white,
      ),
      child: Row(
        children: [
          AnimatedContainer(
            duration: const Duration(milliseconds: 180),
            width: 42,
            height: 42,
            decoration: BoxDecoration(
              color: AppColors.device,
              borderRadius: AppRadius.smBorder,
            ),
            child: Icon(device.type.icon, color: Colors.white, size: 22),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Container(
              constraints: const BoxConstraints(minHeight: 70),
              padding: const EdgeInsets.fromLTRB(0, 12, 6, 12),
              decoration: BoxDecoration(
                border: showDivider
                    ? const Border(
                        bottom: BorderSide(
                          color: AppColors.divider,
                          width: 0.7,
                        ),
                      )
                    : null,
              ),
              child: Row(
                children: [
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Text(
                          device.name,
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis,
                          style: const TextStyle(
                            fontSize: 17,
                            fontWeight: FontWeight.w600,
                            color: AppColors.textPrimary,
                          ),
                        ),
                        const SizedBox(height: 4),
                        Text(
                          availability ??
                              (busy
                                  ? connected
                                        ? '已连接，正在读取数据'
                                        : '正在连接设备'
                                  : '最近同步：${_formatLastSync(device.lastSyncAt)}'),
                          style: TextStyle(
                            fontSize: 13,
                            fontWeight: FontWeight.w600,
                            color: availability == null
                                ? AppColors.textHint
                                : AppColors.textSecondary,
                          ),
                        ),
                      ],
                    ),
                  ),
                  if (connected)
                    Container(
                      margin: const EdgeInsets.only(right: 2),
                      padding: const EdgeInsets.symmetric(
                        horizontal: 9,
                        vertical: 5,
                      ),
                      decoration: BoxDecoration(
                        color: AppColors.successLight,
                        borderRadius: AppRadius.pillBorder,
                      ),
                      child: const Text(
                        '已连接',
                        style: TextStyle(
                          fontSize: 12,
                          height: 1,
                          fontWeight: FontWeight.w600,
                          color: AppColors.successText,
                        ),
                      ),
                    ),
                  IconButton(
                    tooltip: '解绑设备',
                    onPressed: busy ? null : onUnbind,
                    icon: const Icon(Icons.delete_outline_rounded, size: 21),
                    color: AppColors.textHint,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  String _formatLastSync(DateTime? value) {
    if (value == null) return '暂无';
    final now = DateTime.now();
    final dayLabel =
        value.year == now.year &&
            value.month == now.month &&
            value.day == now.day
        ? '今天'
        : '${value.month}-${value.day}';
    final minute = value.minute.toString().padLeft(2, '0');
    return '$dayLabel ${value.hour}:$minute';
  }
}

class _EmptyDevices extends StatelessWidget {
  const _EmptyDevices();

  @override
  Widget build(BuildContext context) {
    return Container(
      width: double.infinity,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: AppRadius.lgBorder,
      ),
      child: AppEmptyState(
        icon: _deviceVisual.icon,
        iconColor: _deviceVisual.color,
        title: '还没有绑定设备',
        subtitle: '点击右上角添加设备',
      ),
    );
  }
}

class _SyncStatusBar extends StatelessWidget {
  final String message;
  final bool active;
  final Future<void> Function()? onRetry;

  const _SyncStatusBar({
    required this.message,
    required this.active,
    required this.onRetry,
  });

  @override
  Widget build(BuildContext context) => Container(
    padding: const EdgeInsets.fromLTRB(14, 11, 8, 11),
    decoration: BoxDecoration(
      color: active ? AppColors.deviceLight : Colors.white,
      borderRadius: AppRadius.mdBorder,
      border: Border.all(color: AppColors.borderLight),
    ),
    child: Row(
      children: [
        Icon(
          active ? Icons.sync_rounded : Icons.info_outline_rounded,
          size: 20,
          color: active ? AppColors.device : AppColors.textSecondary,
        ),
        const SizedBox(width: 10),
        Expanded(
          child: Text(
            message,
            style: const TextStyle(
              fontSize: 13,
              fontWeight: FontWeight.w700,
              color: AppColors.textSecondary,
            ),
          ),
        ),
        if (!active && onRetry != null)
          TextButton(onPressed: onRetry, child: const Text('重试')),
      ],
    ),
  );
}
