import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shadcn_ui/shadcn_ui.dart';
import '../core/app_colors.dart';
import '../core/app_design_tokens.dart';
import '../core/app_module_visuals.dart';
import '../core/app_theme.dart';
import '../core/navigation_provider.dart';
import '../providers/auth_provider.dart';
import '../providers/data_providers.dart';
import '../widgets/common_widgets.dart';
import '../widgets/app_empty_state.dart';
import '../widgets/app_error_state.dart';
import '../widgets/app_future_view.dart';
import '../widgets/app_toast.dart';
import 'diet/diet_nutrition_widgets.dart';
import 'diet/diet_record_logic.dart';

/// 饮食记录列表（趋势+日历+胶囊详情+侧滑编辑删除）
class DietRecordListPage extends ConsumerStatefulWidget {
  const DietRecordListPage({super.key});
  @override
  ConsumerState<DietRecordListPage> createState() => _DietRecordListPageState();
}

class _DietRecordListPageState extends ConsumerState<DietRecordListPage> {
  List<Map<String, dynamic>> _data = [];
  bool _loading = true;
  int _trendDays = 7;
  DateTime _selectedDate = DateTime.now();

  @override
  void initState() {
    super.initState();
    _refresh();
  }

  Future<void> _refresh() async {
    final records = await ref.read(dietServiceProvider).getRecords();
    if (mounted) {
      setState(() {
        _data = records;
        _loading = false;
      });
    }
  }

  Future<void> _delete(String id) async {
    final index = _data.indexWhere((d) => d['id']?.toString() == id);
    if (index < 0) return;
    final removed = _data[index];
    setState(() => _data.removeAt(index));
    try {
      await ref.read(dietServiceProvider).deleteRecord(id);
      ref.invalidate(dietRecordsProvider);
      if (mounted) {
        AppToast.show(context, '已删除', type: AppToastType.success);
      }
    } catch (_) {
      if (!mounted) return;
      setState(() => _data.insert(index.clamp(0, _data.length), removed));
      AppToast.show(context, '删除失败，请稍后重试', type: AppToastType.error);
    }
  }

  String _dateKey(DateTime d) =>
      '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';
  int _dayCalories(DateTime d) {
    final key = _dateKey(d);
    return _data
        .where((r) => (r['recordedAt']?.toString() ?? '').startsWith(key))
        .fold(0, (s, r) => s + ((r['totalCalories'] as num?)?.toInt() ?? 0));
  }

  List<Map<String, dynamic>> _dayRecords(DateTime d) {
    final key = _dateKey(d);
    return _data
        .where((r) => (r['recordedAt']?.toString() ?? '').startsWith(key))
        .toList();
  }

  List<double> _trendData() => List.generate(
    _trendDays,
    (i) => _dayCalories(
      DateTime.now().subtract(Duration(days: _trendDays - 1 - i)),
    ).toDouble(),
  );

  @override
  Widget build(BuildContext context) {
    if (_loading) {
      return GradientScaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          elevation: 0,
          surfaceTintColor: Colors.transparent,
          title: const Text(
            '饮食记录',
            style: TextStyle(color: AppColors.textPrimary),
          ),
          leading: IconButton(
            icon: const Icon(Icons.arrow_back, color: AppColors.textPrimary),
            onPressed: () => popRoute(ref),
          ),
        ),
        body: const Center(child: CircularProgressIndicator()),
      );
    }

    final todayRecords = _dayRecords(_selectedDate);
    final isSelectedToday = _dateKey(_selectedDate) == _dateKey(DateTime.now());
    final totalCal = todayRecords.fold(
      0,
      (s, r) => s + ((r['totalCalories'] as num?)?.toInt() ?? 0),
    );

    return GradientScaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        surfaceTintColor: Colors.transparent,
        title: const Text(
          '饮食记录',
          style: TextStyle(color: AppColors.textPrimary),
        ),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back, color: AppColors.textPrimary),
          onPressed: () => popRoute(ref),
        ),
      ),
      body: ListView(
        padding: const EdgeInsets.fromLTRB(16, 14, 16, 32),
        children: [
          _DietDaySummary(
            date: _selectedDate,
            totalCalories: totalCal,
            count: todayRecords.length,
          ),
          const SizedBox(height: 12),
          _buildDateStrip(),
          const SizedBox(height: 18),
          Row(
            children: [
              Text(
                isSelectedToday
                    ? '今天的饮食'
                    : '${_selectedDate.month}月${_selectedDate.day}日饮食',
                style: const TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w600,
                  color: AppColors.textPrimary,
                ),
              ),
              const Spacer(),
              Text(
                '${todayRecords.length} 条 · $totalCal 千卡',
                style: const TextStyle(
                  fontSize: 13,
                  fontWeight: FontWeight.w600,
                  color: AppColors.textHint,
                ),
              ),
            ],
          ),
          const SizedBox(height: 8),
          if (todayRecords.isEmpty)
            const _DietEmptyDay()
          else
            ClipRRect(
              borderRadius: AppRadius.mdBorder,
              child: ColoredBox(
                color: Colors.white,
                child: Column(
                  children: [
                    for (var i = 0; i < todayRecords.length; i++)
                      _buildDietRecordRow(
                        todayRecords[i],
                        showDivider: i < todayRecords.length - 1,
                      ),
                  ],
                ),
              ),
            ),
          const SizedBox(height: 18),
          _DietTrendPanel(
            trendDays: _trendDays,
            trendData: _trendData(),
            onSelectDays: (days) => setState(() => _trendDays = days),
          ),
        ],
      ),
    );
  }

  Widget _buildDateStrip() {
    final dates = recentDietDates(DateTime.now());
    return Row(
      children: List.generate(dates.length, (i) {
        final d = dates[i];
        final cal = _dayCalories(d);
        final isToday = _dateKey(d) == _dateKey(DateTime.now());
        final isSel = _dateKey(d) == _dateKey(_selectedDate);
        return Expanded(
          child: Padding(
            padding: EdgeInsets.only(right: i == dates.length - 1 ? 0 : 5),
            child: GestureDetector(
              onTap: () => setState(() => _selectedDate = d),
              child: CustomPaint(
                foregroundPainter: cal > 0
                    ? const _DietDateGradientBorderPainter()
                    : null,
                child: Container(
                  padding: const EdgeInsets.symmetric(vertical: 8),
                  decoration: BoxDecoration(
                    color: isSel
                        ? AppColors.primary
                        : (isToday ? DietPalette.primarySoft : Colors.white),
                    borderRadius: AppRadius.mdBorder,
                    border: cal > 0
                        ? null
                        : Border.all(
                            color: isSel
                                ? Colors.transparent
                                : (isToday
                                      ? const Color(0xFFC9D0FF)
                                      : AppColors.borderLight),
                          ),
                  ),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      FittedBox(
                        fit: BoxFit.scaleDown,
                        child: Text(
                          isToday
                              ? '今天'
                              : ['一', '二', '三', '四', '五', '六', '日'][d.weekday -
                                    1],
                          style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.w700,
                            color: isSel ? Colors.white : AppColors.textHint,
                          ),
                        ),
                      ),
                      const SizedBox(height: 3),
                      Text(
                        '${d.day}',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.w700,
                          color: isSel ? Colors.white : AppColors.textPrimary,
                        ),
                      ),
                      if (cal > 0) ...[
                        const SizedBox(height: 2),
                        Text(
                          '$cal',
                          style: TextStyle(
                            fontSize: 11,
                            fontWeight: FontWeight.w600,
                            color: isSel ? Colors.white70 : AppColors.primary,
                          ),
                        ),
                      ],
                    ],
                  ),
                ),
              ),
            ),
          ),
        );
      }),
    );
  }

  Widget _buildDietRecordRow(
    Map<String, dynamic> record, {
    required bool showDivider,
  }) {
    final items =
        (record['foodItems'] as List?)?.cast<Map<String, dynamic>>() ?? [];
    final mealNames = {
      'Breakfast': '早餐',
      'Lunch': '午餐',
      'Dinner': '晚餐',
      'Snack': '加餐',
    };
    final mealIcons = {
      'Breakfast': Icons.wb_sunny,
      'Lunch': Icons.wb_sunny_outlined,
      'Dinner': Icons.nights_stay,
      'Snack': Icons.cookie,
    };
    final mealLabel = mealNames[record['mealType']?.toString()] ?? '';
    final mealIcon =
        mealIcons[record['mealType']?.toString()] ?? Icons.restaurant;
    final cal = record['totalCalories'] ?? 0;
    final id = record['id']?.toString() ?? '';

    return _SwipeAction(
      itemKey: ValueKey(id),
      onEdit: () => _showEditDialog(id, cal),
      onDelete: () => _delete(id),
      child: Material(
        color: Colors.white,
        child: InkWell(
          onTap: () => pushRoute(ref, 'dietDetail', params: {'id': id}),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
            child: Column(
              children: [
                Row(
                  children: [
                    Container(
                      width: 36,
                      height: 36,
                      decoration: BoxDecoration(
                        color: DietPalette.primarySoft,
                        borderRadius: AppRadius.smBorder,
                      ),
                      child: Icon(
                        mealIcon,
                        size: 19,
                        color: DietPalette.primary,
                      ),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Text(
                                mealLabel,
                                style: const TextStyle(
                                  fontSize: 17,
                                  fontWeight: FontWeight.w700,
                                  color: AppColors.textPrimary,
                                ),
                              ),
                              const SizedBox(width: 8),
                              Text(
                                '$cal 千卡',
                                style: const TextStyle(
                                  fontSize: 15,
                                  fontWeight: FontWeight.w600,
                                  color: AppColors.textSecondary,
                                ),
                              ),
                            ],
                          ),
                          if (items.isNotEmpty)
                            Padding(
                              padding: const EdgeInsets.only(top: 3),
                              child: Text(
                                items.map((f) => f['name']).join(' · '),
                                style: const TextStyle(
                                  fontSize: 14,
                                  fontWeight: FontWeight.w500,
                                  color: AppColors.textHint,
                                ),
                                maxLines: 1,
                                overflow: TextOverflow.ellipsis,
                              ),
                            ),
                        ],
                      ),
                    ),
                  ],
                ),
                if (showDivider)
                  const Padding(
                    padding: EdgeInsets.only(left: 48, top: 12),
                    child: Divider(
                      height: 1,
                      thickness: 0.7,
                      color: Color(0xFFE8ECF2),
                    ),
                  ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  void _showEditDialog(String id, num cal) {
    final ctrl = TextEditingController(text: '$cal');
    showDialog(
      context: context,
      builder: (ctx) => AlertDialog(
        title: const Text('修改热量'),
        content: TextField(
          controller: ctrl,
          keyboardType: TextInputType.number,
          decoration: const InputDecoration(labelText: '热量(千卡)'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(ctx),
            child: const Text('取消'),
          ),
          TextButton(
            onPressed: () async {
              Navigator.pop(ctx);
              await ref.read(dietServiceProvider).updateRecord(id, {
                'totalCalories': int.tryParse(ctrl.text) ?? 0,
              });
              _refresh();
            },
            child: const Text('保存'),
          ),
        ],
      ),
    );
  }
}

class _DietDaySummary extends StatelessWidget {
  final DateTime date;
  final int totalCalories;
  final int count;

  const _DietDaySummary({
    required this.date,
    required this.totalCalories,
    required this.count,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.fromLTRB(16, 15, 16, 14),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: AppRadius.xlBorder,
      ),
      child: Row(
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  '${date.month}月${date.day}日',
                  style: const TextStyle(
                    fontSize: 20,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textPrimary,
                  ),
                ),
                const SizedBox(height: 5),
                Text(
                  '共 $totalCalories 千卡 · $count 条记录',
                  style: const TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textSecondary,
                  ),
                ),
              ],
            ),
          ),
          Container(
            width: 42,
            height: 42,
            decoration: BoxDecoration(
              color: DietPalette.primarySoft,
              borderRadius: AppRadius.mdBorder,
            ),
            child: Icon(
              AppModuleVisuals.diet.icon,
              size: 22,
              color: DietPalette.primary,
            ),
          ),
        ],
      ),
    );
  }
}

class _DietEmptyDay extends StatelessWidget {
  const _DietEmptyDay();

  @override
  Widget build(BuildContext context) => Container(
    width: double.infinity,
    padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 18),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: AppRadius.mdBorder,
    ),
    child: Row(
      children: [
        Container(
          width: 42,
          height: 42,
          decoration: BoxDecoration(
            color: DietPalette.primarySoft,
            shape: BoxShape.circle,
          ),
          child: Icon(
            AppModuleVisuals.diet.icon,
            size: 21,
            color: DietPalette.primary,
          ),
        ),
        const SizedBox(width: 12),
        const Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                '当天没有饮食记录',
                style: TextStyle(
                  fontSize: 15,
                  fontWeight: FontWeight.w700,
                  color: AppColors.textPrimary,
                ),
              ),
              SizedBox(height: 3),
              Text(
                '记录后会在这里汇总热量和食物明细',
                style: TextStyle(fontSize: 13, color: AppColors.textHint),
              ),
            ],
          ),
        ),
      ],
    ),
  );
}

class _DietTrendPanel extends StatelessWidget {
  final int trendDays;
  final List<double> trendData;
  final ValueChanged<int> onSelectDays;

  const _DietTrendPanel({
    required this.trendDays,
    required this.trendData,
    required this.onSelectDays,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.fromLTRB(14, 13, 14, 14),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: AppRadius.xlBorder,
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              const Text(
                '热量趋势',
                style: TextStyle(
                  fontSize: 15,
                  fontWeight: FontWeight.w600,
                  color: AppColors.primary,
                ),
              ),
              const Spacer(),
              _Toggle('7天', trendDays == 7, () => onSelectDays(7)),
              const SizedBox(width: 6),
              _Toggle('30天', trendDays == 30, () => onSelectDays(30)),
            ],
          ),
          const SizedBox(height: 12),
          SizedBox(height: 112, child: _TrendChart(trendData)),
        ],
      ),
    );
  }
}

class _Toggle extends StatelessWidget {
  final String l;
  final bool a;
  final VoidCallback t;
  const _Toggle(this.l, this.a, this.t);
  @override
  Widget build(BuildContext c) => GestureDetector(
    onTap: t,
    child: Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
      decoration: BoxDecoration(
        color: a ? AppColors.primary : Colors.white,
        borderRadius: AppRadius.mdBorder,
        border: Border.all(color: a ? Colors.transparent : AppColors.border),
      ),
      child: Text(
        l,
        style: TextStyle(
          fontSize: 12,
          color: a ? Colors.white : AppColors.textSecondary,
        ),
      ),
    ),
  );
}

class _TrendChart extends StatelessWidget {
  final List<double> data;
  const _TrendChart(this.data);
  @override
  Widget build(BuildContext c) {
    if (data.isEmpty) {
      return const Center(
        child: Text('暂无数据', style: TextStyle(color: AppColors.textHint)),
      );
    }
    final m = data.reduce((a, b) => a > b ? a : b);
    if (m == 0) {
      return const Center(
        child: Text('暂无数据', style: TextStyle(color: AppColors.textHint)),
      );
    }
    return Row(
      crossAxisAlignment: CrossAxisAlignment.end,
      children: List.generate(data.length, (i) {
        final h = (data[i] / m * 100).clamp(4.0, 100.0);
        return Expanded(
          child: Padding(
            padding: EdgeInsets.symmetric(
              horizontal: data.length > 15 ? 1.0 : 2.0,
            ),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                if (data.length <= 14)
                  Text(
                    '${data[i].toInt()}',
                    style: const TextStyle(
                      fontSize: 8,
                      color: AppColors.primary,
                    ),
                  ),
                const SizedBox(height: 2),
                Container(
                  height: h,
                  decoration: BoxDecoration(
                    color: data[i] > 0 ? null : AppColors.border,
                    gradient: data[i] > 0 ? AppColors.primaryGradient : null,
                    borderRadius: BorderRadius.circular(2),
                  ),
                ),
              ],
            ),
          ),
        );
      }),
    );
  }
}

class _DietDateGradientBorderPainter extends CustomPainter {
  const _DietDateGradientBorderPainter();

  static const _strokeWidth = 1.5;

  @override
  void paint(Canvas canvas, Size size) {
    final rect = (Offset.zero & size).deflate(_strokeWidth / 2);
    final paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = _strokeWidth
      ..shader = AppColors.primaryGradient.createShader(rect)
      ..isAntiAlias = true;
    canvas.drawRRect(
      RRect.fromRectAndRadius(
        rect,
        const Radius.circular(AppTheme.rMd - _strokeWidth / 2),
      ),
      paint,
    );
  }

  @override
  bool shouldRepaint(_DietDateGradientBorderPainter oldDelegate) => false;
}

class _SwipeAction extends StatelessWidget {
  final Key itemKey;
  final Widget child;
  final VoidCallback onEdit;
  final VoidCallback onDelete;
  const _SwipeAction({
    required this.itemKey,
    required this.child,
    required this.onEdit,
    required this.onDelete,
  });
  @override
  Widget build(BuildContext c) => Dismissible(
    key: itemKey,
    direction: DismissDirection.horizontal,
    confirmDismiss: (direction) async {
      if (dietRecordSwipeAction(direction) == DietRecordSwipeAction.edit) {
        onEdit();
        return false;
      }
      return true;
    },
    background: Container(
      decoration: BoxDecoration(
        borderRadius: AppRadius.mdBorder,
        color: const Color(0xFF3B82F6),
      ),
      alignment: Alignment.centerLeft,
      padding: const EdgeInsets.only(left: 20),
      child: const Icon(Icons.edit, color: Colors.white),
    ),
    secondaryBackground: Container(
      decoration: BoxDecoration(
        borderRadius: AppRadius.mdBorder,
        color: AppColors.error,
      ),
      alignment: Alignment.centerRight,
      padding: const EdgeInsets.only(right: 20),
      child: const Icon(Icons.delete, color: Colors.white),
    ),
    onDismissed: (_) => onDelete(),
    child: child,
  );
}

/// 饮食记录详情页
class DietRecordDetailPage extends ConsumerStatefulWidget {
  final String id;
  const DietRecordDetailPage({super.key, required this.id});

  @override
  ConsumerState<DietRecordDetailPage> createState() =>
      _DietRecordDetailPageState();
}

class _DietRecordDetailPageState extends ConsumerState<DietRecordDetailPage> {
  String _formatRecordedAt(Object? value) {
    final date = DateTime.tryParse(value?.toString() ?? '')?.toLocal();
    if (date == null) return '';
    final now = DateTime.now();
    final prefix = date.year == now.year
        ? '${date.month}-${date.day}'
        : '${date.year}-${date.month}-${date.day}';
    return '$prefix ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
  }

  @override
  Widget build(BuildContext context) {
    return GradientScaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => popRoute(ref),
        ),
        title: const Text('饮食详情'),
      ),
      body: AppAsyncValueView<List<Map<String, dynamic>>>(
        value: ref.watch(dietRecordsProvider),
        errorTitle: '饮食记录加载失败',
        onRetry: () => ref.invalidate(dietRecordsProvider),
        onData: (ctx, records) {
          final d = records.firstWhere(
            (r) => r['id']?.toString() == widget.id,
            orElse: () => <String, dynamic>{},
          );
          if (d.isEmpty) {
            return AppEmptyState(
              icon: AppModuleVisuals.diet.icon,
              iconColor: DietPalette.primary,
              title: '这条饮食记录不存在',
              subtitle: '记录可能已经被删除',
            );
          }
          final items =
              (d['foodItems'] as List?)?.cast<Map<String, dynamic>>() ?? [];
          final totalCal = (d['totalCalories'] as num?)?.toInt() ?? 0;
          final protein = (totalCal * 0.16 / 4).round();
          final carbs = (totalCal * 0.52 / 4).round();
          final fat = (totalCal * 0.28 / 9).round();
          final mealNames = {
            'Breakfast': '早餐',
            'Lunch': '午餐',
            'Dinner': '晚餐',
            'Snack': '加餐',
          };
          return ListView(
            padding: const EdgeInsets.fromLTRB(16, 14, 16, 32),
            children: [
              Container(
                padding: const EdgeInsets.fromLTRB(16, 16, 16, 15),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: AppRadius.xlBorder,
                ),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Row(
                      children: [
                        Expanded(
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text(
                                mealNames[d['mealType']?.toString()] ?? '饮食记录',
                                style: const TextStyle(
                                  fontSize: 18,
                                  fontWeight: FontWeight.w600,
                                  color: AppColors.textPrimary,
                                ),
                              ),
                              Text(
                                _formatRecordedAt(d['recordedAt']),
                                style: const TextStyle(
                                  fontSize: 13,
                                  color: AppColors.textHint,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 20),
                    Row(
                      crossAxisAlignment: CrossAxisAlignment.center,
                      children: [
                        DietCalorieRing(calories: totalCal),
                        const SizedBox(width: 20),
                        Expanded(
                          child: Column(
                            children: [
                              DietMacroBar(
                                label: '蛋白质',
                                value: protein,
                                color: DietPalette.protein,
                                reference: 50,
                              ),
                              const SizedBox(height: 14),
                              DietMacroBar(
                                label: '碳水',
                                value: carbs,
                                color: DietPalette.carbs,
                                reference: 100,
                              ),
                              const SizedBox(height: 14),
                              DietMacroBar(
                                label: '脂肪',
                                value: fat,
                                color: DietPalette.fat,
                                reference: 35,
                              ),
                            ],
                          ),
                        ),
                      ],
                    ),
                    const SizedBox(height: 8),
                    const Text(
                      '营养数据根据本餐总热量估算，仅供日常记录参考',
                      style: TextStyle(fontSize: 11, color: AppColors.textHint),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 20),
              const Text('识别结果', style: AppTextStyles.sectionTitle),
              const SizedBox(height: 8),
              ClipRRect(
                borderRadius: AppRadius.xlBorder,
                child: ColoredBox(
                  color: Colors.white,
                  child: Column(
                    children: [
                      if (items.isEmpty)
                        const Padding(
                          padding: EdgeInsets.all(18),
                          child: Text(
                            '这条记录没有食物明细',
                            style: TextStyle(color: AppColors.textHint),
                          ),
                        ),
                      for (var i = 0; i < items.length; i++) ...[
                        Padding(
                          padding: const EdgeInsets.symmetric(
                            horizontal: 15,
                            vertical: 14,
                          ),
                          child: Row(
                            children: [
                              Expanded(
                                child: Text(
                                  items[i]['name']?.toString() ?? '',
                                  style: const TextStyle(
                                    fontSize: 16,
                                    fontWeight: FontWeight.w700,
                                    color: AppColors.textPrimary,
                                  ),
                                ),
                              ),
                              SizedBox(
                                width: 86,
                                child: Text(
                                  items[i]['portion']?.toString() ?? '',
                                  textAlign: TextAlign.center,
                                  style: const TextStyle(
                                    fontSize: 16,
                                    fontWeight: FontWeight.w600,
                                    color: AppColors.textSecondary,
                                  ),
                                ),
                              ),
                              SizedBox(
                                width: 78,
                                child: Text(
                                  '${items[i]['calories'] ?? 0} 千卡',
                                  textAlign: TextAlign.right,
                                  style: const TextStyle(
                                    fontSize: 16,
                                    fontWeight: FontWeight.w700,
                                    color: DietPalette.calorie,
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ),
                        if (i < items.length - 1)
                          const Padding(
                            padding: EdgeInsets.only(left: 15),
                            child: Divider(
                              height: 1,
                              thickness: 0.7,
                              color: Color(0xFFE8ECF2),
                            ),
                          ),
                      ],
                    ],
                  ),
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

/// 运动计划列表（含打卡）
class ExercisePlanPage extends ConsumerStatefulWidget {
  const ExercisePlanPage({super.key});
  @override
  ConsumerState<ExercisePlanPage> createState() => _ExercisePlanPageState();
}

class _ExercisePlanPageState extends ConsumerState<ExercisePlanPage> {
  static const _exerciseVisual = AppModuleVisuals.exercise;

  Future<List<Map<String, dynamic>>>? _future;
  final Set<String> _busyItems = {};

  @override
  void initState() {
    super.initState();
    _load();
  }

  void _load() => setState(() {
    _future = ref.read(exerciseServiceProvider).getPlans();
  });

  Future<void> _refresh() async {
    _load();
    await _future;
  }

  Future<void> _checkIn(String itemId) async {
    if (itemId.isEmpty || _busyItems.contains(itemId)) return;
    setState(() => _busyItems.add(itemId));
    try {
      await ref.read(exerciseServiceProvider).checkIn(itemId);
      ref.invalidate(currentExercisePlanProvider);
      _load();
    } catch (e) {
      debugPrint('[ExercisePlan] 打卡失败: $e');
      if (mounted) {
        AppToast.show(context, '只能打卡今天的运动任务', type: AppToastType.error);
      }
    } finally {
      if (mounted) setState(() => _busyItems.remove(itemId));
    }
  }

  Future<void> _deletePlan(String id) async {
    await ref.read(exerciseServiceProvider).deletePlan(id);
    ref.invalidate(currentExercisePlanProvider);
    _load();
  }

  String _todayKey() {
    final now = DateTime.now();
    return '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
  }

  @override
  Widget build(BuildContext context) {
    return GradientScaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => popRoute(ref),
        ),
        title: const Text('运动计划'),
      ),
      floatingActionButton: _ModuleGradientFab(
        gradient: _exerciseVisual.gradient,
        shadowColor: _exerciseVisual.color,
        onTap: () => pushRoute(ref, 'exerciseCreate'),
      ),
      body: AppFutureView<List<Map<String, dynamic>>>(
        future: _future,
        onRetry: _load,
        errorTitle: '运动计划加载失败',
        onData: (ctx, plans) {
          if (plans.isEmpty) {
            return _empty(context, '运动计划', '暂无计划，点击右下角新建');
          }
          final todayKey = _todayKey();
          final todayItems = plans
              .expand(
                (p) =>
                    ((p['items'] as List?)?.cast<Map<String, dynamic>>() ??
                    <Map<String, dynamic>>[]),
              )
              .where((item) => item['scheduledDate']?.toString() == todayKey)
              .toList();
          final todayDone = todayItems
              .where((item) => item['isCompleted'] == true)
              .length;

          return RefreshIndicator(
            onRefresh: _refresh,
            child: ListView(
              physics: const AlwaysScrollableScrollPhysics(),
              padding: const EdgeInsets.fromLTRB(16, 12, 16, 88),
              children: [
                _ExerciseCheckInSummary(
                  planCount: plans.length,
                  totalToday: todayItems.length,
                  doneToday: todayDone,
                ),
                const SizedBox(height: 10),
                ...List.generate(plans.length, (i) {
                  final p = plans[i];
                  final items =
                      (p['items'] as List?)?.cast<Map<String, dynamic>>() ?? [];
                  if (items.isEmpty) return const SizedBox.shrink();

                  return SwipeDeleteTile(
                    key: Key(p['id']?.toString() ?? '$i'),
                    onDelete: () => _deletePlan(p['id']?.toString() ?? ''),
                    margin: const EdgeInsets.symmetric(vertical: 6),
                    child: _ExercisePlanOverviewCard(
                      plan: p,
                      items: items,
                      todayKey: todayKey,
                      busyItems: _busyItems,
                      onCheckIn: _checkIn,
                    ),
                  );
                }),
              ],
            ),
          );
        },
      ),
    );
  }
}

class _ModuleGradientFab extends StatelessWidget {
  final Gradient gradient;
  final Color shadowColor;
  final VoidCallback onTap;

  const _ModuleGradientFab({
    required this.gradient,
    required this.shadowColor,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 56,
      height: 56,
      decoration: BoxDecoration(
        gradient: gradient,
        borderRadius: AppRadius.lgBorder,
        boxShadow: [
          BoxShadow(
            color: shadowColor.withValues(alpha: 0.22),
            blurRadius: 16,
            offset: const Offset(0, 8),
          ),
        ],
      ),
      child: Material(
        color: Colors.transparent,
        borderRadius: AppRadius.lgBorder,
        child: InkWell(
          onTap: onTap,
          borderRadius: AppRadius.lgBorder,
          child: const Icon(Icons.add, size: 28, color: Colors.white),
        ),
      ),
    );
  }
}

class _ExercisePlanOverviewCard extends StatelessWidget {
  final Map<String, dynamic> plan;
  final List<Map<String, dynamic>> items;
  final String todayKey;
  final Set<String> busyItems;
  final Future<void> Function(String itemId) onCheckIn;

  const _ExercisePlanOverviewCard({
    required this.plan,
    required this.items,
    required this.todayKey,
    required this.busyItems,
    required this.onCheckIn,
  });

  @override
  Widget build(BuildContext context) {
    final sortedItems = [...items]
      ..sort(
        (a, b) => (a['scheduledDate']?.toString() ?? '').compareTo(
          b['scheduledDate']?.toString() ?? '',
        ),
      );
    final total = sortedItems.length;
    final done = sortedItems.where((it) => it['isCompleted'] == true).length;
    final progress = total == 0 ? 0.0 : done / total;
    final todayItem = sortedItems.firstWhere(
      (it) => it['scheduledDate']?.toString() == todayKey,
      orElse: () => <String, dynamic>{},
    );
    final exerciseName = _exerciseSummary(sortedItems);
    final startDate = plan['startDate']?.toString() ?? '';
    final endDate = plan['endDate']?.toString() ?? '';

    return Container(
      width: double.infinity,
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: AppColors.border, width: 1.1),
        boxShadow: [AppTheme.shadowLight],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Container(
                width: 44,
                height: 44,
                decoration: BoxDecoration(
                  gradient: _ExercisePlanPageState._exerciseVisual.gradient,
                  borderRadius: BorderRadius.circular(14),
                ),
                child: Icon(
                  _ExercisePlanPageState._exerciseVisual.icon,
                  color: Colors.white,
                  size: 24,
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      exerciseName,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(
                        fontSize: 19,
                        fontWeight: FontWeight.w600,
                        color: AppColors.textPrimary,
                      ),
                    ),
                    const SizedBox(height: 3),
                    Text(
                      '${_slashDate(startDate)} - ${_slashDate(endDate)}',
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(
                        fontSize: 16,
                        color: AppTheme.textSub,
                      ),
                    ),
                  ],
                ),
              ),
              Container(
                padding: const EdgeInsets.symmetric(
                  horizontal: 10,
                  vertical: 5,
                ),
                decoration: BoxDecoration(
                  color: _ExercisePlanPageState._exerciseVisual.lightColor,
                  borderRadius: BorderRadius.circular(999),
                  border: Border.all(
                    color: _ExercisePlanPageState._exerciseVisual.borderColor,
                  ),
                ),
                child: Text(
                  '$done/$total 天',
                  style: TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w600,
                    color: _ExercisePlanPageState._exerciseVisual.color,
                  ),
                ),
              ),
            ],
          ),
          const SizedBox(height: 14),
          ClipRRect(
            borderRadius: BorderRadius.circular(999),
            child: LinearProgressIndicator(
              minHeight: 8,
              value: progress.clamp(0.0, 1.0),
              backgroundColor: AppColors.cardInner,
              valueColor: AlwaysStoppedAnimation<Color>(
                _ExercisePlanPageState._exerciseVisual.color,
              ),
            ),
          ),
          const SizedBox(height: 14),
          _ExerciseTodayInlineTask(
            item: todayItem,
            isBusy: busyItems.contains(todayItem['id']?.toString() ?? ''),
            onCheckIn: onCheckIn,
          ),
        ],
      ),
    );
  }

  String _exerciseSummary(List<Map<String, dynamic>> source) {
    final names = source
        .where((item) => item['isRestDay'] != true)
        .map((item) => item['exerciseType']?.toString() ?? '')
        .where((name) => name.isNotEmpty)
        .toSet()
        .toList();
    if (names.isEmpty) return '运动计划';
    if (names.length == 1) return names.first;
    return '${names.first}等 ${names.length} 项';
  }
}

class _ExerciseTodayInlineTask extends StatelessWidget {
  final Map<String, dynamic> item;
  final bool isBusy;
  final Future<void> Function(String itemId) onCheckIn;

  const _ExerciseTodayInlineTask({
    required this.item,
    required this.isBusy,
    required this.onCheckIn,
  });

  @override
  Widget build(BuildContext context) {
    final hasItem = item.isNotEmpty;
    final isCompleted = item['isCompleted'] == true;
    final isRestDay = item['isRestDay'] == true;
    final itemId = item['id']?.toString() ?? '';
    final name = item['exerciseType']?.toString() ?? '今日休息';
    final minutes = ((item['durationMinutes'] as num?)?.toInt() ?? 0);
    final canCheckIn = hasItem && !isRestDay;

    return Container(
      padding: const EdgeInsets.fromLTRB(12, 11, 10, 11),
      decoration: BoxDecoration(
        color: isCompleted ? AppColors.successLight : const Color(0xFFF8FAFC),
        borderRadius: BorderRadius.circular(12),
        border: Border.all(
          color: isCompleted
              ? AppTheme.success.withValues(alpha: 0.18)
              : AppColors.borderLight,
        ),
      ),
      child: Row(
        children: [
          Icon(
            isCompleted ? Icons.check_circle : Icons.today_outlined,
            size: 22,
            color: isCompleted
                ? AppTheme.success
                : _ExercisePlanPageState._exerciseVisual.color,
          ),
          const SizedBox(width: 10),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  '今日任务',
                  style: TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textSecondary,
                  ),
                ),
                const SizedBox(height: 2),
                Text(
                  !hasItem
                      ? '今天没有安排'
                      : (isRestDay ? '休息日' : '$name · $minutes 分钟'),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.w600,
                    color: AppColors.textPrimary,
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(width: 8),
          SizedBox(
            height: 38,
            child: FilledButton.icon(
              onPressed: canCheckIn && !isBusy ? () => onCheckIn(itemId) : null,
              style: FilledButton.styleFrom(
                backgroundColor: isCompleted
                    ? Colors.white
                    : _ExercisePlanPageState._exerciseVisual.color,
                foregroundColor: isCompleted ? AppTheme.success : Colors.white,
                disabledBackgroundColor: AppColors.cardInner,
                disabledForegroundColor: AppColors.textHint,
                padding: const EdgeInsets.symmetric(horizontal: 12),
                minimumSize: const Size(0, 38),
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(10),
                  side: BorderSide(
                    color: isCompleted
                        ? AppTheme.success.withValues(alpha: 0.34)
                        : Colors.transparent,
                  ),
                ),
              ),
              icon: isBusy
                  ? const SizedBox(
                      width: 14,
                      height: 14,
                      child: CircularProgressIndicator(
                        strokeWidth: 2,
                        color: AppColors.textHint,
                      ),
                    )
                  : Icon(
                      isCompleted ? Icons.undo_rounded : Icons.check_rounded,
                      size: 17,
                    ),
              label: Text(
                !hasItem || isRestDay ? '今日无' : (isCompleted ? '撤销' : '打卡'),
                style: const TextStyle(
                  fontSize: 14,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _ExerciseCheckInSummary extends StatelessWidget {
  final int planCount;
  final int totalToday;
  final int doneToday;

  const _ExerciseCheckInSummary({
    required this.planCount,
    required this.totalToday,
    required this.doneToday,
  });

  @override
  Widget build(BuildContext context) {
    final pending = totalToday - doneToday;
    final progress = totalToday == 0 ? 0.0 : doneToday / totalToday;

    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: AppColors.border, width: 1.1),
        boxShadow: [AppTheme.shadowLight],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              const Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      '今日运动进度',
                      style: TextStyle(
                        fontSize: 19,
                        fontWeight: FontWeight.w600,
                        color: AppColors.textPrimary,
                      ),
                    ),
                    SizedBox(height: 2),
                    Text(
                      '只允许打卡今天的运动任务',
                      style: TextStyle(fontSize: 16, color: AppTheme.textSub),
                    ),
                  ],
                ),
              ),
              Text(
                '${(progress * 100).round()}%',
                style: TextStyle(
                  fontSize: 22,
                  fontWeight: FontWeight.w700,
                  color: _ExercisePlanPageState._exerciseVisual.color,
                ),
              ),
            ],
          ),
          const SizedBox(height: 16),
          ClipRRect(
            borderRadius: BorderRadius.circular(999),
            child: LinearProgressIndicator(
              minHeight: 9,
              value: progress,
              backgroundColor: AppColors.cardInner,
              valueColor: AlwaysStoppedAnimation<Color>(
                _ExercisePlanPageState._exerciseVisual.color,
              ),
            ),
          ),
          const SizedBox(height: 14),
          Row(
            children: [
              Expanded(
                child: _ExerciseSummaryStat(
                  label: '待完成',
                  value: '$pending',
                  icon: Icons.schedule_outlined,
                  color: AppColors.warningText,
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: _ExerciseSummaryStat(
                  label: '已打卡',
                  value: '$doneToday',
                  icon: Icons.check_circle_outline,
                  color: AppColors.successText,
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: _ExerciseSummaryStat(
                  label: '计划数',
                  value: '$planCount',
                  icon: Icons.view_week_outlined,
                  color: _ExercisePlanPageState._exerciseVisual.color,
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class _ExerciseSummaryStat extends StatelessWidget {
  final String label;
  final String value;
  final IconData icon;
  final Color color;

  const _ExerciseSummaryStat({
    required this.label,
    required this.value,
    required this.icon,
    required this.color,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      constraints: const BoxConstraints(minHeight: 58),
      padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 9),
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.08),
        borderRadius: BorderRadius.circular(10),
        border: Border.all(color: color.withValues(alpha: 0.14)),
      ),
      child: Row(
        children: [
          Icon(icon, color: color, size: 17),
          const SizedBox(width: 7),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  value,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.w600,
                    color: AppColors.textPrimary,
                  ),
                ),
                Text(
                  label,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: const TextStyle(
                    fontSize: 13,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textSecondary,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class ExercisePlanDetailPage extends ConsumerStatefulWidget {
  final String id;
  const ExercisePlanDetailPage({super.key, required this.id});

  @override
  ConsumerState<ExercisePlanDetailPage> createState() =>
      _ExercisePlanDetailPageState();
}

class _ExercisePlanDetailPageState
    extends ConsumerState<ExercisePlanDetailPage> {
  Future<Map<String, dynamic>?>? _future;
  final Set<String> _busyItems = {};

  static const _exerciseVisual = AppModuleVisuals.exercise;
  @override
  void initState() {
    super.initState();
    _load();
  }

  void _load() {
    setState(() {
      _future = widget.id.isEmpty
          ? Future<Map<String, dynamic>?>.value(null)
          : ref.read(exerciseServiceProvider).getPlanDetail(widget.id);
    });
  }

  Future<void> _checkIn(String itemId) async {
    if (itemId.isEmpty || _busyItems.contains(itemId)) return;
    setState(() => _busyItems.add(itemId));
    try {
      await ref.read(exerciseServiceProvider).checkIn(itemId);
      ref.invalidate(currentExercisePlanProvider);
      _load();
    } catch (e) {
      debugPrint('[ExercisePlanDetail] 打卡失败: $e');
      if (mounted) {
        AppToast.show(context, '只能打卡今天的运动任务', type: AppToastType.error);
      }
    } finally {
      if (mounted) setState(() => _busyItems.remove(itemId));
    }
  }

  String _todayKey() {
    final now = DateTime.now();
    return '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')}';
  }

  @override
  Widget build(BuildContext context) {
    return GradientScaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => popRoute(ref),
        ),
        title: const Text('运动计划详情'),
      ),
      body: AppFutureView<Map<String, dynamic>?>(
        future: _future,
        onRetry: _load,
        errorTitle: '运动计划详情加载失败',
        onData: (ctx, plan) {
          if (plan == null) {
            return _empty(context, '运动计划详情', '没有找到这条运动计划');
          }

          final items =
              (plan['items'] as List?)?.cast<Map<String, dynamic>>() ?? [];
          final total = items.length;
          final done = items.where((it) => it['isCompleted'] == true).length;
          final progress = total == 0 ? 0.0 : done / total;
          final today = _todayKey();
          final todayItem = items.firstWhere(
            (it) => it['scheduledDate']?.toString() == today,
            orElse: () => <String, dynamic>{},
          );
          final exerciseName = items.isNotEmpty
              ? (items.first['exerciseType']?.toString() ?? '运动')
              : '运动';
          final startDate = plan['startDate']?.toString() ?? '';
          final endDate = plan['endDate']?.toString() ?? '';

          return RefreshIndicator(
            onRefresh: () async => _load(),
            child: ListView(
              padding: const EdgeInsets.fromLTRB(16, 12, 16, 28),
              children: [
                _ExerciseHeroCard(
                  exerciseName: exerciseName,
                  startDate: startDate,
                  endDate: endDate,
                  durationMinutes: items.isNotEmpty
                      ? ((items.first['durationMinutes'] as num?)?.toInt() ?? 0)
                      : 0,
                  done: done,
                  total: total,
                  progress: progress,
                ),
                const SizedBox(height: 12),
                _TodayExerciseCard(
                  item: todayItem,
                  isBusy: _busyItems.contains(
                    todayItem['id']?.toString() ?? '',
                  ),
                  onCheckIn: () => _checkIn(todayItem['id']?.toString() ?? ''),
                ),
                const SizedBox(height: 16),
                const Text(
                  '每日安排',
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textPrimary,
                  ),
                ),
                const SizedBox(height: 10),
                if (items.isEmpty)
                  _empty(context, '每日安排', '这条计划还没有安排内容')
                else
                  ...items.map(
                    (item) => _ExerciseDayTile(
                      date: item['scheduledDate']?.toString() ?? '',
                      name: item['exerciseType']?.toString() ?? '运动',
                      minutes:
                          ((item['durationMinutes'] as num?)?.toInt() ?? 0),
                      isCompleted: item['isCompleted'] == true,
                      isToday: item['scheduledDate']?.toString() == today,
                      isRestDay: item['isRestDay'] == true,
                      isBusy: _busyItems.contains(item['id']?.toString() ?? ''),
                      onTap: item['scheduledDate']?.toString() == today
                          ? () => _checkIn(item['id']?.toString() ?? '')
                          : null,
                    ),
                  ),
              ],
            ),
          );
        },
      ),
    );
  }
}

class _ExerciseHeroCard extends StatelessWidget {
  final String exerciseName;
  final String startDate;
  final String endDate;
  final int durationMinutes;
  final int done;
  final int total;
  final double progress;

  const _ExerciseHeroCard({
    required this.exerciseName,
    required this.startDate,
    required this.endDate,
    required this.durationMinutes,
    required this.done,
    required this.total,
    required this.progress,
  });

  String _slashDate(String value) {
    if (value.length >= 10) {
      return value.substring(0, 10).replaceAll('-', '/');
    }
    return value.replaceAll('-', '/');
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: AppColors.border),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Container(
                width: 42,
                height: 42,
                decoration: BoxDecoration(
                  gradient:
                      _ExercisePlanDetailPageState._exerciseVisual.gradient,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Icon(
                  _ExercisePlanDetailPageState._exerciseVisual.icon,
                  color: Colors.white,
                  size: 24,
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      exerciseName,
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(
                        color: AppColors.textPrimary,
                        fontSize: 20,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    const SizedBox(height: 4),
                    Text(
                      '${_slashDate(startDate)} 至 ${_slashDate(endDate)} · $durationMinutes分钟/天',
                      maxLines: 1,
                      overflow: TextOverflow.ellipsis,
                      style: const TextStyle(
                        color: AppColors.textSecondary,
                        fontSize: 13,
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(width: 8),
              Container(
                padding: const EdgeInsets.symmetric(
                  horizontal: 10,
                  vertical: 5,
                ),
                decoration: BoxDecoration(
                  color:
                      _ExercisePlanDetailPageState._exerciseVisual.lightColor,
                  borderRadius: BorderRadius.circular(999),
                  border: Border.all(
                    color: _ExercisePlanDetailPageState
                        ._exerciseVisual
                        .borderColor,
                  ),
                ),
                child: Text(
                  '$done/$total 天',
                  style: TextStyle(
                    color: _ExercisePlanDetailPageState._exerciseVisual.color,
                    fontSize: 13,
                    fontWeight: FontWeight.w700,
                  ),
                ),
              ),
            ],
          ),
          const SizedBox(height: 14),
          ClipRRect(
            borderRadius: BorderRadius.circular(999),
            child: LinearProgressIndicator(
              minHeight: 8,
              value: progress.clamp(0.0, 1.0),
              backgroundColor:
                  _ExercisePlanDetailPageState._exerciseVisual.lightColor,
              valueColor: AlwaysStoppedAnimation<Color>(
                _ExercisePlanDetailPageState._exerciseVisual.color,
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _TodayExerciseCard extends StatelessWidget {
  final Map<String, dynamic> item;
  final bool isBusy;
  final VoidCallback onCheckIn;

  const _TodayExerciseCard({
    required this.item,
    required this.isBusy,
    required this.onCheckIn,
  });

  @override
  Widget build(BuildContext context) {
    final hasItem = item.isNotEmpty;
    final isDone = item['isCompleted'] == true;
    final isRestDay = item['isRestDay'] == true;
    final name = item['exerciseType']?.toString() ?? '今日休息';
    final minutes = ((item['durationMinutes'] as num?)?.toInt() ?? 0);
    final canCheckIn = hasItem && !isRestDay;

    return Container(
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: AppColors.border, width: 1.1),
        boxShadow: [AppTheme.shadowLight],
      ),
      child: Row(
        children: [
          Container(
            width: 38,
            height: 38,
            decoration: BoxDecoration(
              color: isDone
                  ? AppColors.successLight
                  : _ExercisePlanDetailPageState._exerciseVisual.lightColor,
              borderRadius: BorderRadius.circular(11),
            ),
            child: Icon(
              isDone ? Icons.check_circle : Icons.today_outlined,
              color: isDone
                  ? AppTheme.success
                  : _ExercisePlanDetailPageState._exerciseVisual.color,
              size: 23,
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  hasItem ? '今日任务' : '今日无任务',
                  style: const TextStyle(
                    fontSize: 12,
                    color: AppColors.textSecondary,
                    fontWeight: FontWeight.w600,
                  ),
                ),
                const SizedBox(height: 3),
                Text(
                  hasItem ? '$name · $minutes 分钟' : '可以好好恢复，保持轻量活动',
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: const TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.w600,
                    color: AppColors.textPrimary,
                  ),
                ),
              ],
            ),
          ),
          if (hasItem)
            SizedBox(
              height: 38,
              child: FilledButton.icon(
                onPressed: canCheckIn && !isBusy ? onCheckIn : null,
                style: FilledButton.styleFrom(
                  backgroundColor: isDone
                      ? Colors.white
                      : _ExercisePlanDetailPageState._exerciseVisual.color,
                  foregroundColor: isDone ? AppTheme.success : Colors.white,
                  disabledBackgroundColor: AppColors.cardInner,
                  disabledForegroundColor: AppColors.textHint,
                  padding: const EdgeInsets.symmetric(horizontal: 12),
                  minimumSize: const Size(0, 38),
                  shape: RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(10),
                    side: BorderSide(
                      color: isDone
                          ? AppTheme.success.withValues(alpha: 0.34)
                          : Colors.transparent,
                    ),
                  ),
                ),
                icon: isBusy
                    ? const SizedBox(
                        width: 14,
                        height: 14,
                        child: CircularProgressIndicator(
                          strokeWidth: 2,
                          color: AppColors.textHint,
                        ),
                      )
                    : Icon(
                        isDone ? Icons.undo_rounded : Icons.check_rounded,
                        size: 17,
                      ),
                label: Text(
                  isRestDay ? '休息' : (isDone ? '撤销' : '打卡'),
                  style: const TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w600,
                  ),
                ),
              ),
            ),
        ],
      ),
    );
  }
}

class _ExerciseDayTile extends StatelessWidget {
  final String date;
  final String name;
  final int minutes;
  final bool isCompleted;
  final bool isToday;
  final bool isRestDay;
  final bool isBusy;
  final VoidCallback? onTap;

  const _ExerciseDayTile({
    required this.date,
    required this.name,
    required this.minutes,
    required this.isCompleted,
    required this.isToday,
    required this.isRestDay,
    required this.isBusy,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) {
    final canCheckIn = isToday && !isRestDay;
    final subtitle = isRestDay ? '$date · 休息日' : '$date · $minutes 分钟';

    return Container(
      margin: const EdgeInsets.only(bottom: 8),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(14),
        border: Border.all(
          color: isToday
              ? _ExercisePlanDetailPageState._exerciseVisual.color
              : AppColors.borderLight,
          width: isToday ? 1.3 : 1,
        ),
      ),
      child: ListTile(
        contentPadding: const EdgeInsets.symmetric(horizontal: 14, vertical: 4),
        leading: Container(
          width: 38,
          height: 38,
          decoration: BoxDecoration(
            color: isCompleted
                ? AppColors.successLight
                : _ExercisePlanDetailPageState._exerciseVisual.lightColor,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Icon(
            isCompleted
                ? Icons.check
                : _ExercisePlanDetailPageState._exerciseVisual.icon,
            color: isCompleted
                ? AppTheme.success
                : _ExercisePlanDetailPageState._exerciseVisual.color,
            size: 21,
          ),
        ),
        title: Text(
          isToday ? '$name · 今天' : name,
          maxLines: 1,
          overflow: TextOverflow.ellipsis,
          style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
        ),
        subtitle: Text(
          subtitle,
          style: const TextStyle(fontSize: 13, color: AppColors.textHint),
        ),
        trailing: SizedBox(
          height: 36,
          child: FilledButton.icon(
            onPressed: canCheckIn && !isBusy ? onTap : null,
            style: FilledButton.styleFrom(
              backgroundColor: isCompleted
                  ? Colors.white
                  : _ExercisePlanDetailPageState._exerciseVisual.color,
              foregroundColor: isCompleted ? AppTheme.success : Colors.white,
              disabledBackgroundColor: AppColors.cardInner,
              disabledForegroundColor: AppColors.textHint,
              padding: const EdgeInsets.symmetric(horizontal: 10),
              minimumSize: const Size(0, 36),
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(10),
                side: BorderSide(
                  color: isCompleted
                      ? AppTheme.success.withValues(alpha: 0.34)
                      : Colors.transparent,
                ),
              ),
            ),
            icon: isBusy
                ? const SizedBox(
                    width: 14,
                    height: 14,
                    child: CircularProgressIndicator(
                      strokeWidth: 2,
                      color: AppColors.textHint,
                    ),
                  )
                : Icon(
                    isCompleted ? Icons.undo_rounded : Icons.check_rounded,
                    size: 16,
                  ),
            label: Text(
              !isToday
                  ? '只读'
                  : (isRestDay ? '休息' : (isCompleted ? '撤销' : '打卡')),
              style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600),
            ),
          ),
        ),
      ),
    );
  }
}

/// 新建运动计划
class ExercisePlanCreatePage extends ConsumerStatefulWidget {
  const ExercisePlanCreatePage({super.key});
  @override
  ConsumerState<ExercisePlanCreatePage> createState() =>
      _ExercisePlanCreatePageState();
}

class _ExercisePlanCreatePageState
    extends ConsumerState<ExercisePlanCreatePage> {
  final _nameCtrl = TextEditingController();
  final _durationCtrl = TextEditingController(text: '30');
  final _daysCtrl = TextEditingController(text: '7');
  DateTime _start = DateTime.now();
  TimeOfDay _reminderTime = const TimeOfDay(hour: 19, minute: 0);
  @override
  void dispose() {
    _nameCtrl.dispose();
    _durationCtrl.dispose();
    _daysCtrl.dispose();
    super.dispose();
  }

  Future<void> _save() async {
    final name = _nameCtrl.text.trim();
    if (name.isEmpty) {
      AppToast.show(context, '请输入运动名称', type: AppToastType.warning);
      return;
    }
    final dur = int.tryParse(_durationCtrl.text) ?? 30;
    final days = (int.tryParse(_daysCtrl.text) ?? 7).clamp(1, 366);
    final end = _start.add(Duration(days: days - 1));
    await ref.read(exerciseServiceProvider).createPlanSimple({
      'exerciseType': name,
      'durationMinutes': dur,
      'startDate':
          '${_start.year}-${_start.month.toString().padLeft(2, '0')}-${_start.day.toString().padLeft(2, '0')}',
      'endDate':
          '${end.year}-${end.month.toString().padLeft(2, '0')}-${end.day.toString().padLeft(2, '0')}',
      'reminderTime':
          '${_reminderTime.hour.toString().padLeft(2, '0')}:${_reminderTime.minute.toString().padLeft(2, '0')}',
    });
    ref.invalidate(currentExercisePlanProvider);
    if (mounted) {
      popRoute(ref);
    }
  }

  @override
  Widget build(BuildContext context) {
    return GradientScaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => popRoute(ref),
        ),
        title: const Text('新建计划'),
      ),
      body: ListView(
        padding: AppSpacing.page,
        children: [
          Container(
            padding: AppSpacing.panel,
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: AppRadius.lgBorder,
            ),
            child: Row(
              children: [
                Container(
                  width: 44,
                  height: 44,
                  decoration: BoxDecoration(
                    color: AppModuleVisuals.exercise.lightColor,
                    borderRadius: AppRadius.smBorder,
                  ),
                  child: Icon(
                    AppModuleVisuals.exercise.icon,
                    color: AppModuleVisuals.exercise.color,
                    size: 23,
                  ),
                ),
                const SizedBox(width: 12),
                const Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('制定运动计划', style: AppTextStyles.sectionTitle),
                      SizedBox(height: 3),
                      Text('设置运动内容、周期和每日提醒', style: AppTextStyles.listSubtitle),
                    ],
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(height: 14),
          Container(
            padding: AppSpacing.panel,
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: AppRadius.lgBorder,
            ),
            child: Column(
              children: [
                _field('运动名称', _nameCtrl, hint: '如：散步、慢跑、太极'),
                const SizedBox(height: 16),
                Row(
                  children: [
                    Expanded(
                      child: _field(
                        '每天时长（分钟）',
                        _durationCtrl,
                        hint: '30',
                        number: true,
                      ),
                    ),
                    const SizedBox(width: 12),
                    Expanded(
                      child: _field('坚持天数', _daysCtrl, hint: '7', number: true),
                    ),
                  ],
                ),
                const SizedBox(height: 16),
                _dateField('开始日期', _start, (d) => setState(() => _start = d)),
                const SizedBox(height: 16),
                _timeField(),
              ],
            ),
          ),
          const SizedBox(height: 18),
          AppGradientOutlineButton(label: '保存计划', onPressed: _save),
          const SizedBox(height: 18),
        ],
      ),
    );
  }

  Widget _field(
    String label,
    TextEditingController ctrl, {
    String? hint,
    bool number = false,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          label,
          style: const TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w700,
            color: AppColors.textPrimary,
          ),
        ),
        const SizedBox(height: 6),
        TextField(
          controller: ctrl,
          keyboardType: number ? TextInputType.number : null,
          decoration: InputDecoration(
            hintText: hint,
            filled: true,
            fillColor: AppColors.cardInner,
            border: OutlineInputBorder(
              borderRadius: AppRadius.mdBorder,
              borderSide: BorderSide.none,
            ),
            enabledBorder: OutlineInputBorder(
              borderRadius: AppRadius.mdBorder,
              borderSide: BorderSide.none,
            ),
            focusedBorder: OutlineInputBorder(
              borderRadius: AppRadius.mdBorder,
              borderSide: const BorderSide(
                color: AppColors.primary,
                width: 1.5,
              ),
            ),
          ),
          style: const TextStyle(fontSize: 16),
        ),
      ],
    );
  }

  Widget _dateField(
    String label,
    DateTime val,
    ValueChanged<DateTime> onChanged,
  ) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          label,
          style: const TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w700,
            color: AppColors.textPrimary,
          ),
        ),
        const SizedBox(height: 6),
        GestureDetector(
          onTap: () async {
            final d = await showAppDatePicker(
              context,
              initialDate: val,
              firstDate: DateTime(2024),
              lastDate: DateTime(2030),
            );
            if (d != null) onChanged(d);
          },
          child: Container(
            width: double.infinity,
            padding: const EdgeInsets.all(14),
            decoration: BoxDecoration(
              color: AppColors.cardInner,
              borderRadius: AppRadius.mdBorder,
            ),
            child: Row(
              children: [
                Expanded(
                  child: Text(
                    _displayDate(val),
                    style: const TextStyle(fontSize: 16),
                  ),
                ),
                const Icon(
                  LucideIcons.calendarDays,
                  size: 19,
                  color: AppColors.primaryDark,
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }

  Widget _timeField() {
    final value =
        '${_reminderTime.hour.toString().padLeft(2, '0')}:${_reminderTime.minute.toString().padLeft(2, '0')}';
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          '每日提醒时间',
          style: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w700,
            color: AppColors.textPrimary,
          ),
        ),
        const SizedBox(height: 6),
        GestureDetector(
          onTap: () async {
            final picked = await showAppTimePicker(
              context,
              initialTime: _reminderTime,
            );
            if (picked != null && mounted) {
              setState(() => _reminderTime = picked);
            }
          },
          child: Container(
            width: double.infinity,
            padding: const EdgeInsets.all(14),
            decoration: BoxDecoration(
              color: AppColors.cardInner,
              borderRadius: AppRadius.mdBorder,
            ),
            child: Row(
              children: [
                Expanded(
                  child: Text(value, style: const TextStyle(fontSize: 16)),
                ),
                const Icon(
                  LucideIcons.clock,
                  size: 19,
                  color: AppColors.primaryDark,
                ),
              ],
            ),
          ),
        ),
      ],
    );
  }
}

/// 复查列表（从服务器读取）
class FollowUpListPage extends ConsumerStatefulWidget {
  const FollowUpListPage({super.key});
  @override
  ConsumerState<FollowUpListPage> createState() => _FollowUpListPageState();
}

class _FollowUpListPageState extends ConsumerState<FollowUpListPage> {
  @override
  Widget build(BuildContext context) {
    return GradientScaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: const Icon(Icons.arrow_back),
          onPressed: () => popRoute(ref),
        ),
        title: const Text('复查随访'),
        centerTitle: true,
      ),
      body: AppAsyncValueView<List<Map<String, dynamic>>>(
        value: ref.watch(followUpListProvider),
        errorTitle: '随访加载失败',
        onRetry: () => ref.invalidate(followUpListProvider),
        onData: (ctx, list) {
          if (list.isEmpty) {
            return Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(
                    Icons.event_note_outlined,
                    size: 64,
                    color: AppColors.textHint,
                  ),
                  const SizedBox(height: 12),
                  Text('暂无随访安排', style: Theme.of(context).textTheme.bodyMedium),
                  const SizedBox(height: 4),
                  const Text(
                    '医生创建随访后将显示在这里',
                    style: TextStyle(fontSize: 16, color: AppColors.textHint),
                  ),
                ],
              ),
            );
          }
          return ListView(
            padding: const EdgeInsets.symmetric(vertical: 8),
            children: list.map((item) => _FollowUpItem(item: item)).toList(),
          );
        },
      ),
    );
  }
}

class _FollowUpItem extends StatelessWidget {
  final Map<String, dynamic> item;
  const _FollowUpItem({required this.item});

  String _statusLabel(String? status) {
    switch (status) {
      case 'Upcoming':
        return '即将到来';
      case 'Completed':
        return '已完成';
      case 'Cancelled':
        return '已取消';
      default:
        return status ?? '';
    }
  }

  Color _statusColor(String? status) {
    switch (status) {
      case 'Upcoming':
        return AppColors.primary;
      case 'Completed':
        return AppTheme.success;
      case 'Cancelled':
        return AppTheme.error;
      default:
        return AppColors.textHint;
    }
  }

  Color _statusBg(String? status) {
    switch (status) {
      case 'Upcoming':
        return AppColors.iconBg;
      case 'Completed':
        return AppColors.successLight;
      case 'Cancelled':
        return AppColors.errorLight;
      default:
        return AppColors.backgroundSoft;
    }
  }

  @override
  Widget build(BuildContext context) {
    final status = item['status']?.toString();
    final label = _statusLabel(status);
    final color = _statusColor(status);
    final bg = _statusBg(status);

    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(AppTheme.rMd),
        boxShadow: [AppTheme.shadowCard],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            children: [
              Container(
                padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                decoration: BoxDecoration(
                  color: bg,
                  borderRadius: BorderRadius.circular(AppTheme.rSm),
                ),
                child: Text(
                  label,
                  style: TextStyle(
                    fontSize: 15,
                    color: color,
                    fontWeight: FontWeight.w500,
                  ),
                ),
              ),
              const Spacer(),
              if (item['doctorName'] != null)
                Text(
                  '👨‍⚕️ ${item['doctorName']}',
                  style: const TextStyle(
                    fontSize: 15,
                    color: AppColors.textHint,
                  ),
                ),
            ],
          ),
          const SizedBox(height: 10),
          Text(
            item['title']?.toString() ?? '',
            style: const TextStyle(fontSize: 19, fontWeight: FontWeight.w600),
          ),
          const SizedBox(height: 4),
          if (item['scheduledAt'] != null)
            Text(
              _formatDateTime(item['scheduledAt']?.toString()),
              style: const TextStyle(fontSize: 17, color: AppColors.textHint),
            ),
          if ((item['notes']?.toString() ?? '').isNotEmpty) ...[
            const SizedBox(height: 8),
            Text(
              item['notes']?.toString() ?? '',
              style: const TextStyle(
                fontSize: 16,
                color: AppColors.textSecondary,
              ),
            ),
          ],
        ],
      ),
    );
  }

  String _formatDateTime(String? iso) {
    if (iso == null) return '';
    final dt = DateTime.tryParse(iso);
    if (dt == null) return iso;
    return '${dt.year}-${dt.month.toString().padLeft(2, '0')}-${dt.day.toString().padLeft(2, '0')} ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}';
  }
}

/// 健康档案（可编辑）
List<String> normalizeArchiveItems(String input) {
  final values = input
      .split(RegExp(r'[、,，;；\n]'))
      .map((value) => value.trim())
      .where((value) => value.isNotEmpty)
      .toSet()
      .toList();
  if (values.length > 1) values.remove('无');
  return values;
}

class HealthArchivePage extends ConsumerStatefulWidget {
  const HealthArchivePage({super.key});
  @override
  ConsumerState<HealthArchivePage> createState() => _HealthArchivePageState();
}

class _HealthArchivePageState extends ConsumerState<HealthArchivePage> {
  final _nameCtrl = TextEditingController();
  final _genderCtrl = TextEditingController();
  String _birthDate = '';
  final _diagnosisCtrl = TextEditingController();
  final List<Map<String, String>> _surgeries = [];
  final _allergiesCtrl = TextEditingController();
  final _chronicCtrl = TextEditingController();
  final _dietCtrl = TextEditingController();
  final _familyCtrl = TextEditingController();
  bool _loading = true;
  bool _saving = false;
  String? _loadError;
  String? _surgeryHistoryStatus;
  String? _updatedAt;
  String _savedSignature = '';
  int _nextSurgeryId = 0;

  @override
  void initState() {
    super.initState();
    _load();
  }

  @override
  void dispose() {
    _nameCtrl.dispose();
    _genderCtrl.dispose();
    _diagnosisCtrl.dispose();
    _allergiesCtrl.dispose();
    _chronicCtrl.dispose();
    _dietCtrl.dispose();
    _familyCtrl.dispose();
    super.dispose();
  }

  Future<void> _load() async {
    final srv = ref.read(userServiceProvider);
    try {
      final results = await Future.wait([
        srv.getProfile(),
        srv.getHealthArchive(),
      ]);
      final p = results[0];
      final a = results[1];
      if (!mounted) return;
      if (p != null) {
        _nameCtrl.text = p['name'] ?? '';
        _genderCtrl.text = p['gender'] ?? '';
        _birthDate = p['birthDate'] ?? '';
      }
      _surgeries.clear();
      if (a != null) {
        _diagnosisCtrl.text = a['diagnosis'] ?? '';
        final surgeries = a['surgeries'] as List?;
        if (surgeries != null && surgeries.isNotEmpty) {
          _surgeries.addAll(
            surgeries.whereType<Map>().map(
              (item) => {
                'localId': '${_nextSurgeryId++}',
                'type': item['type']?.toString() ?? '',
                'date': item['date']?.toString() ?? '',
              },
            ),
          );
        } else {
          final st = a['surgeryType'] as String?;
          final sd = a['surgeryDate'] as String?;
          if (st != null && st.isNotEmpty) {
            _surgeries.add({
              'localId': '${_nextSurgeryId++}',
              'type': st,
              'date': sd ?? '',
            });
          }
        }
        _allergiesCtrl.text = (a['allergies'] as List?)?.join('、') ?? '';
        _chronicCtrl.text = (a['chronicDiseases'] as List?)?.join('、') ?? '';
        _dietCtrl.text = (a['dietRestrictions'] as List?)?.join('、') ?? '';
        _familyCtrl.text = a['familyHistory'] ?? '';
        _surgeryHistoryStatus = a['surgeryHistoryStatus']?.toString();
        _updatedAt = a['updatedAt']?.toString();
      }
      _surgeryHistoryStatus ??= _surgeries.isNotEmpty ? '有' : null;
      _savedSignature = _formSignature();
      setState(() {
        _loading = false;
        _loadError = null;
      });
    } catch (_) {
      if (mounted) {
        setState(() {
          _loading = false;
          _loadError = '健康档案加载失败';
        });
      }
    }
  }

  Future<void> _pickDate(
    void Function(String) onPicked, {
    String current = '',
  }) async {
    final now = DateTime.now();
    final parsedCurrent = DateTime.tryParse(current);
    final d = await showAppDatePicker(
      context,
      initialDate: parsedCurrent ?? DateTime(now.year - 30),
      firstDate: DateTime(1900),
      lastDate: now,
    );
    if (d != null) {
      onPicked(
        '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}',
      );
    }
  }

  void _addSurgery() => setState(() {
    _surgeryHistoryStatus = '有';
    _surgeries.add({'localId': '${_nextSurgeryId++}', 'type': '', 'date': ''});
  });
  void _removeSurgery(int i) => setState(() => _surgeries.removeAt(i));
  void _setGender(String value) => setState(() => _genderCtrl.text = value);

  void _setSurgeryHistoryStatus(String value) {
    setState(() {
      _surgeryHistoryStatus = value;
      if (value == '无') _surgeries.clear();
      if (value == '有' && _surgeries.isEmpty) {
        _surgeries.add({
          'localId': '${_nextSurgeryId++}',
          'type': '',
          'date': '',
        });
      }
    });
  }

  int get _completionPercent {
    final values = [
      _nameCtrl.text.trim(),
      _genderCtrl.text.trim(),
      _birthDate,
      _diagnosisCtrl.text.trim(),
      _allergiesCtrl.text.trim(),
      _chronicCtrl.text.trim(),
      _dietCtrl.text.trim(),
      _familyCtrl.text.trim(),
      _surgeryHistoryStatus ?? '',
    ];
    final completed = values.where((value) => value.isNotEmpty).length;
    return (completed * 100 / values.length).round();
  }

  String _formSignature() => [
    _nameCtrl.text.trim(),
    _genderCtrl.text,
    _birthDate,
    _diagnosisCtrl.text.trim(),
    _allergiesCtrl.text.trim(),
    _chronicCtrl.text.trim(),
    _dietCtrl.text.trim(),
    _familyCtrl.text.trim(),
    _surgeryHistoryStatus ?? '',
    ..._surgeries.expand(
      (s) => [(s['type'] ?? '').trim(), (s['date'] ?? '').trim()],
    ),
  ].join('|');

  bool get _hasUnsavedChanges => _formSignature() != _savedSignature;

  Future<void> _save() async {
    if (_nameCtrl.text.trim().isEmpty) {
      AppToast.show(context, '请填写姓名', type: AppToastType.warning);
      return;
    }
    if (_surgeryHistoryStatus == '有' &&
        _surgeries.any((s) => (s['type'] ?? '').trim().isEmpty)) {
      AppToast.show(context, '请填写手术名称', type: AppToastType.warning);
      return;
    }
    setState(() => _saving = true);
    var profileSaved = false;
    try {
      final srv = ref.read(userServiceProvider);
      await srv.updateProfile(
        name: _nameCtrl.text,
        gender: _genderCtrl.text,
        birthDate: _birthDate,
      );
      profileSaved = true;
      final surgeries = _surgeries
          .where((s) => (s['type'] ?? '').trim().isNotEmpty)
          .map(
            (s) => {
              'type': s['type']!.trim(),
              'date': (s['date'] ?? '').trim().isEmpty
                  ? null
                  : s['date']!.trim(),
            },
          )
          .toList();
      final firstSurgery = surgeries.isEmpty ? null : surgeries.first;
      await srv.updateHealthArchive({
        'diagnosis': _diagnosisCtrl.text,
        'surgeryType': firstSurgery?['type'],
        'surgeryDate': firstSurgery?['date'],
        'surgeries': surgeries,
        'allergies': normalizeArchiveItems(_allergiesCtrl.text),
        'chronicDiseases': normalizeArchiveItems(_chronicCtrl.text),
        'dietRestrictions': normalizeArchiveItems(_dietCtrl.text),
        'familyHistory': _familyCtrl.text,
        'surgeryHistoryStatus': _surgeryHistoryStatus,
      });
      await ref.read(authProvider.notifier).refreshProfile();
      _savedSignature = _formSignature();
      _updatedAt = DateTime.now().toIso8601String();
      if (mounted) {
        AppToast.show(context, '健康档案已保存', type: AppToastType.success);
        setState(() {});
      }
    } catch (_) {
      if (mounted) {
        AppToast.show(
          context,
          profileSaved ? '个人资料已保存，健康信息保存失败' : '保存失败，请稍后重试',
          type: AppToastType.error,
        );
      }
    } finally {
      if (mounted) setState(() => _saving = false);
    }
  }

  Future<void> _handleBack() async {
    if (!_hasUnsavedChanges) {
      popRoute(ref);
      return;
    }
    final leave = await showDialog<bool>(
      context: context,
      builder: (dialogContext) => AlertDialog(
        shape: RoundedRectangleBorder(borderRadius: AppRadius.lgBorder),
        title: const Text('放弃修改？'),
        content: const Text('当前健康档案还有未保存的修改。'),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(dialogContext, false),
            child: const Text('继续编辑'),
          ),
          TextButton(
            onPressed: () => Navigator.pop(dialogContext, true),
            child: const Text(
              '放弃修改',
              style: TextStyle(color: AppColors.errorText),
            ),
          ),
        ],
      ),
    );
    if (leave == true && mounted) popRoute(ref);
  }

  @override
  Widget build(BuildContext context) {
    if (_loading) {
      return GradientScaffold(
        appBar: AppBar(
          backgroundColor: Colors.white,
          elevation: 0,
          surfaceTintColor: Colors.transparent,
          title: const Text(
            '健康档案',
            style: TextStyle(color: AppColors.textPrimary),
          ),
          leading: IconButton(
            icon: const Icon(Icons.arrow_back, color: AppColors.textPrimary),
            onPressed: _handleBack,
          ),
        ),
        body: const Center(child: CircularProgressIndicator()),
      );
    }
    if (_loadError != null) {
      return GradientScaffold(
        appBar: AppBar(
          title: const Text('健康档案'),
          centerTitle: true,
          leading: IconButton(
            icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 19),
            onPressed: () => popRoute(ref),
          ),
        ),
        body: AppErrorState(
          title: _loadError!,
          subtitle: '为避免覆盖原有档案，加载成功前不能编辑',
          onRetry: () {
            setState(() {
              _loading = true;
              _loadError = null;
            });
            _load();
          },
        ),
      );
    }
    return GradientScaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        surfaceTintColor: Colors.transparent,
        title: const Text('健康档案'),
        centerTitle: true,
        leading: IconButton(
          icon: const Icon(Icons.arrow_back_ios_new_rounded, size: 19),
          onPressed: _handleBack,
        ),
      ),
      bottomNavigationBar: _ArchiveSaveBar(saving: _saving, onSave: _save),
      body: ListView(
        keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
        padding: const EdgeInsets.fromLTRB(16, 12, 16, 28),
        children: [
          _ArchiveSummary(
            completion: _completionPercent,
            updatedAt: _updatedAt,
          ),
          const SizedBox(height: 16),
          _Section('个人资料', LucideIcons.userRound, [
            _F('姓名', _nameCtrl, hint: '请输入姓名'),
            const SizedBox(height: 12),
            Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Expanded(
                  child: _GenderF(
                    value: _genderCtrl.text,
                    onChanged: _setGender,
                  ),
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: _DateF(
                    '出生日期',
                    _birthDate,
                    () => _pickDate(
                      (v) => setState(() => _birthDate = v),
                      current: _birthDate,
                    ),
                  ),
                ),
              ],
            ),
          ]),
          const SizedBox(height: 16),
          _Section('医疗经历', LucideIcons.clipboardPlus, [
            _F(
              '主要诊断',
              _diagnosisCtrl,
              hint: '如：冠心病',
              onFillNone: () => _diagnosisCtrl.text = '无',
            ),
            const SizedBox(height: 14),
            Row(
              children: [
                const Text(
                  '手术史',
                  style: TextStyle(
                    fontSize: 15,
                    fontWeight: FontWeight.w700,
                    color: AppColors.textPrimary,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 8),
            _BinaryChoice(
              value: _surgeryHistoryStatus,
              leftLabel: '无手术史',
              leftValue: '无',
              rightLabel: '有手术史',
              rightValue: '有',
              onChanged: _setSurgeryHistoryStatus,
            ),
            if (_surgeryHistoryStatus == '有') ...[
              const SizedBox(height: 8),
              Align(
                alignment: Alignment.centerRight,
                child: _AddBtn(_addSurgery),
              ),
            ],
            ..._surgeries.asMap().entries.map((e) {
              final i = e.key;
              final s = e.value;
              return Padding(
                padding: const EdgeInsets.only(top: 8),
                child: Row(
                  children: [
                    Expanded(
                      child: _F2(
                        '手术',
                        s['type'] ?? '',
                        (v) => s['type'] = v,
                        hint: '如: 心脏搭桥',
                        key: ValueKey(s['localId']),
                      ),
                    ),
                    const SizedBox(width: 8),
                    Expanded(
                      child: _DateF2(
                        '日期',
                        s['date'] ?? '',
                        () => _pickDate(
                          (v) => setState(() => s['date'] = v),
                          current: s['date'] ?? '',
                        ),
                        hint: '点击选择',
                      ),
                    ),
                    GestureDetector(
                      onTap: () => _removeSurgery(i),
                      child: Container(
                        width: 32,
                        height: 32,
                        decoration: BoxDecoration(
                          color: AppColors.errorLight,
                          borderRadius: AppRadius.mdBorder,
                        ),
                        child: const Icon(
                          Icons.close,
                          size: 16,
                          color: AppColors.errorText,
                        ),
                      ),
                    ),
                  ],
                ),
              );
            }),
          ]),
          const SizedBox(height: 16),
          _Section('健康风险', LucideIcons.shieldAlert, [
            _F(
              '过敏史',
              _allergiesCtrl,
              hint: '多个项目用顿号分隔，如：青霉素、海鲜',
              onFillNone: () => _allergiesCtrl.text = '无',
            ),
            const SizedBox(height: 14),
            _F(
              '慢性病史',
              _chronicCtrl,
              hint: '多个项目用顿号分隔，如：高血压、高血脂',
              onFillNone: () => _chronicCtrl.text = '无',
            ),
            const SizedBox(height: 14),
            _F(
              '饮食限制',
              _dietCtrl,
              hint: '多个项目用顿号分隔，如：低盐、低脂',
              onFillNone: () => _dietCtrl.text = '无',
            ),
            const SizedBox(height: 14),
            _F(
              '家族病史',
              _familyCtrl,
              hint: '如：父亲患有冠心病',
              onFillNone: () => _familyCtrl.text = '无',
            ),
          ]),
        ],
      ),
    );
  }
}

class _ArchiveSummary extends StatelessWidget {
  final int completion;
  final String? updatedAt;

  const _ArchiveSummary({required this.completion, required this.updatedAt});

  String get _updatedLabel {
    final date = DateTime.tryParse(updatedAt ?? '')?.toLocal();
    if (date == null) return '尚未保存健康档案';
    return '更新于 ${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
  }

  @override
  Widget build(BuildContext context) => Container(
    padding: const EdgeInsets.all(16),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: AppRadius.lgBorder,
    ),
    child: Row(
      children: [
        SizedBox(
          width: 58,
          height: 58,
          child: Stack(
            alignment: Alignment.center,
            children: [
              CircularProgressIndicator(
                value: completion / 100,
                strokeWidth: 5,
                color: AppColors.primary,
                backgroundColor: AppColors.primaryLight,
              ),
              Text(
                '$completion%',
                style: const TextStyle(
                  fontSize: 13,
                  fontWeight: FontWeight.w600,
                ),
              ),
            ],
          ),
        ),
        const SizedBox(width: 14),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const Text('档案完整度', style: AppTextStyles.listTitle),
              const SizedBox(height: 5),
              Text(_updatedLabel, style: AppTextStyles.listSubtitle),
            ],
          ),
        ),
      ],
    ),
  );
}

class _ArchiveSaveBar extends StatelessWidget {
  final bool saving;
  final VoidCallback onSave;

  const _ArchiveSaveBar({required this.saving, required this.onSave});

  @override
  Widget build(BuildContext context) => SafeArea(
    top: false,
    child: Container(
      padding: const EdgeInsets.fromLTRB(16, 10, 16, 12),
      color: Colors.white,
      child: AppGradientOutlineButton(
        label: '保存修改',
        loading: saving,
        onPressed: saving ? null : onSave,
      ),
    ),
  );
}

class _Section extends StatelessWidget {
  final String title;
  final IconData icon;
  final List<Widget> fields;
  const _Section(this.title, this.icon, this.fields);
  Color get _color => AppColors.primary;
  @override
  Widget build(BuildContext c) => Container(
    padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 14),
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: AppRadius.lgBorder,
    ),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Container(
              width: 28,
              height: 28,
              decoration: BoxDecoration(
                color: _color.withValues(alpha: 0.10),
                borderRadius: AppRadius.smBorder,
              ),
              child: Icon(icon, size: 17, color: _color),
            ),
            const SizedBox(width: 9),
            Text(
              title,
              style: const TextStyle(
                fontSize: 16,
                fontWeight: FontWeight.w600,
                color: AppColors.textPrimary,
              ),
            ),
          ],
        ),
        const SizedBox(height: 11),
        ...fields,
      ],
    ),
  );
}

class _F extends StatelessWidget {
  final String label;
  final TextEditingController ctrl;
  final String? hint;
  final VoidCallback? onFillNone;
  const _F(this.label, this.ctrl, {this.hint, this.onFillNone});
  @override
  Widget build(BuildContext c) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(
        children: [
          Text(
            label,
            style: const TextStyle(
              fontSize: 14,
              fontWeight: FontWeight.w700,
              color: AppColors.textPrimary,
            ),
          ),
          const Spacer(),
          if (onFillNone != null)
            TextButton(
              onPressed: onFillNone,
              style: TextButton.styleFrom(
                minimumSize: const Size(0, 30),
                padding: const EdgeInsets.symmetric(horizontal: 8),
                foregroundColor: AppColors.primary,
              ),
              child: const Text('填无'),
            ),
        ],
      ),
      const SizedBox(height: 5),
      TextField(
        controller: ctrl,
        style: const TextStyle(fontSize: 15),
        decoration: InputDecoration(
          hintText: hint,
          hintStyle: const TextStyle(fontSize: 13, color: AppColors.textHint),
          filled: true,
          fillColor: AppColors.cardInner,
          contentPadding: const EdgeInsets.symmetric(
            horizontal: 14,
            vertical: 11,
          ),
          border: OutlineInputBorder(
            borderRadius: AppRadius.mdBorder,
            borderSide: BorderSide.none,
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: AppRadius.mdBorder,
            borderSide: BorderSide.none,
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: AppRadius.mdBorder,
            borderSide: const BorderSide(color: AppColors.primary),
          ),
        ),
      ),
    ],
  );
}

class _GenderF extends StatelessWidget {
  final String value;
  final ValueChanged<String> onChanged;

  const _GenderF({required this.value, required this.onChanged});

  @override
  Widget build(BuildContext c) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text(
          '性别',
          style: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w700,
            color: AppColors.textPrimary,
          ),
        ),
        const SizedBox(height: 6),
        Container(
          height: 44,
          padding: const EdgeInsets.all(4),
          decoration: BoxDecoration(
            color: AppColors.cardInner,
            borderRadius: AppRadius.mdBorder,
          ),
          child: Row(
            children: [
              Expanded(child: _GenderChip('男', value == '男', onChanged)),
              const SizedBox(width: 4),
              Expanded(child: _GenderChip('女', value == '女', onChanged)),
            ],
          ),
        ),
      ],
    );
  }
}

class _BinaryChoice extends StatelessWidget {
  final String? value;
  final String leftLabel;
  final String leftValue;
  final String rightLabel;
  final String rightValue;
  final ValueChanged<String> onChanged;

  const _BinaryChoice({
    required this.value,
    required this.leftLabel,
    required this.leftValue,
    required this.rightLabel,
    required this.rightValue,
    required this.onChanged,
  });

  @override
  Widget build(BuildContext context) => Container(
    height: 48,
    padding: const EdgeInsets.all(4),
    decoration: BoxDecoration(
      color: AppColors.cardInner,
      borderRadius: AppRadius.mdBorder,
    ),
    child: Row(
      children: [
        Expanded(
          child: _ChoiceItem(
            label: leftLabel,
            selected: value == leftValue,
            onTap: () => onChanged(leftValue),
          ),
        ),
        const SizedBox(width: 4),
        Expanded(
          child: _ChoiceItem(
            label: rightLabel,
            selected: value == rightValue,
            onTap: () => onChanged(rightValue),
          ),
        ),
      ],
    ),
  );
}

class _ChoiceItem extends StatelessWidget {
  final String label;
  final bool selected;
  final VoidCallback onTap;

  const _ChoiceItem({
    required this.label,
    required this.selected,
    required this.onTap,
  });

  @override
  Widget build(BuildContext context) => Material(
    color: selected ? AppColors.primary : Colors.transparent,
    borderRadius: AppRadius.smBorder,
    child: InkWell(
      onTap: onTap,
      borderRadius: AppRadius.smBorder,
      child: Center(
        child: Text(
          label,
          style: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w700,
            color: selected ? Colors.white : AppColors.textSecondary,
          ),
        ),
      ),
    ),
  );
}

class _GenderChip extends StatelessWidget {
  final String label;
  final bool selected;
  final ValueChanged<String> onChanged;

  const _GenderChip(this.label, this.selected, this.onChanged);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => onChanged(label),
      child: Container(
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: selected ? AppColors.primary : Colors.transparent,
          borderRadius: AppRadius.smBorder,
        ),
        child: Text(
          label,
          style: TextStyle(
            fontSize: 14,
            fontWeight: FontWeight.w700,
            color: selected ? Colors.white : AppColors.textSecondary,
          ),
        ),
      ),
    );
  }
}

class _F2 extends StatelessWidget {
  final String label;
  final String value;
  final void Function(String) chg;
  final String? hint;
  const _F2(this.label, this.value, this.chg, {this.hint, super.key});
  @override
  Widget build(BuildContext c) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        label,
        style: const TextStyle(fontSize: 12, color: AppColors.textSecondary),
      ),
      const SizedBox(height: 4),
      TextFormField(
        initialValue: value,
        onChanged: chg,
        style: const TextStyle(fontSize: 14),
        decoration: InputDecoration(
          hintText: hint,
          hintStyle: const TextStyle(fontSize: 13, color: AppColors.textHint),
          filled: true,
          fillColor: AppColors.cardInner,
          contentPadding: const EdgeInsets.symmetric(
            horizontal: 10,
            vertical: 8,
          ),
          border: OutlineInputBorder(
            borderRadius: AppRadius.mdBorder,
            borderSide: BorderSide.none,
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: AppRadius.mdBorder,
            borderSide: BorderSide.none,
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: AppRadius.mdBorder,
            borderSide: const BorderSide(color: AppColors.primary),
          ),
        ),
      ),
    ],
  );
}

class _DateF extends StatelessWidget {
  final String label;
  final String value;
  final VoidCallback onTap;
  const _DateF(this.label, this.value, this.onTap);
  @override
  Widget build(BuildContext c) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        label,
        style: const TextStyle(
          fontSize: 14,
          fontWeight: FontWeight.w700,
          color: AppColors.textPrimary,
        ),
      ),
      const SizedBox(height: 6),
      GestureDetector(
        onTap: onTap,
        child: Container(
          height: 44,
          padding: const EdgeInsets.symmetric(horizontal: 10),
          decoration: BoxDecoration(
            color: AppColors.cardInner,
            borderRadius: AppRadius.mdBorder,
          ),
          alignment: Alignment.centerLeft,
          child: Row(
            children: [
              Expanded(
                child: Text(
                  value.isEmpty ? '选择日期' : _slashDate(value),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(
                    fontSize: 14,
                    color: value.isEmpty
                        ? AppColors.textHint
                        : AppColors.textPrimary,
                  ),
                ),
              ),
              const SizedBox(width: 4),
              const Icon(
                Icons.calendar_today_outlined,
                size: 18,
                color: Color(0xFF7C5CFF),
              ),
            ],
          ),
        ),
      ),
    ],
  );
}

class _DateF2 extends StatelessWidget {
  final String label;
  final String value;
  final VoidCallback onTap;
  final String? hint;
  const _DateF2(this.label, this.value, this.onTap, {this.hint});
  @override
  Widget build(BuildContext c) => Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Text(
        label,
        style: const TextStyle(fontSize: 12, color: AppColors.textSecondary),
      ),
      const SizedBox(height: 4),
      GestureDetector(
        onTap: onTap,
        child: Container(
          height: 44,
          padding: const EdgeInsets.symmetric(horizontal: 10),
          decoration: BoxDecoration(
            color: AppColors.cardInner,
            borderRadius: AppRadius.mdBorder,
          ),
          alignment: Alignment.centerLeft,
          child: Row(
            children: [
              Text(
                value.isEmpty ? (hint ?? '点击选择') : _slashDate(value),
                style: TextStyle(
                  fontSize: 13,
                  color: value.isEmpty
                      ? AppColors.textHint
                      : AppColors.textPrimary,
                ),
              ),
              const Spacer(),
              const Icon(
                Icons.calendar_today_outlined,
                size: 14,
                color: Color(0xFF7C5CFF),
              ),
            ],
          ),
        ),
      ),
    ],
  );
}

String _slashDate(String value) {
  final date = value.trim();
  if (date.length >= 10) return date.substring(0, 10);
  return date;
}

String _displayDate(DateTime date) {
  return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
}

class _AddBtn extends StatelessWidget {
  final VoidCallback onTap;
  const _AddBtn(this.onTap);
  @override
  Widget build(BuildContext c) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
        decoration: BoxDecoration(
          color: AppColors.primarySoft,
          borderRadius: AppRadius.mdBorder,
          border: Border.all(color: AppColors.primaryLight),
        ),
        child: const Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.add, size: 14, color: AppColors.primaryDark),
            SizedBox(width: 2),
            Text(
              '添加',
              style: TextStyle(fontSize: 13, color: AppColors.primaryDark),
            ),
          ],
        ),
      ),
    );
  }
}

/// 健康日历
DateTime calendarDateForMonth(DateTime month, DateTime selectedDate) {
  final lastDay = DateTime(month.year, month.month + 1, 0).day;
  final day = selectedDate.day > lastDay ? lastDay : selectedDate.day;
  return DateTime(month.year, month.month, day);
}

class HealthCalendarPage extends ConsumerStatefulWidget {
  const HealthCalendarPage({super.key});
  @override
  ConsumerState<HealthCalendarPage> createState() => _HealthCalendarPageState();
}

class _HealthCalendarPageState extends ConsumerState<HealthCalendarPage> {
  DateTime _currentMonth = DateTime.now();
  DateTime? _selectedDate = DateTime.now();
  Map<String, List<Map<String, dynamic>>> _events = {};
  Map<String, dynamic>? _selectedDayData;
  final Map<String, Map<String, dynamic>> _dayCache = {};
  bool _loadingMonth = true;
  bool _loadingDay = false;
  String? _monthError;
  String? _dayError;
  int _monthRequestId = 0;
  int _dayRequestId = 0;

  static const _medColor = AppColors.medication;
  static const _exerciseColor = AppColors.exercise;
  static const _followupColor = AppColors.followup;
  static const _calendarColor = AppColors.calendar;

  @override
  void initState() {
    super.initState();
    _loadMonth();
  }

  String _dateKey(DateTime d) =>
      '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')}';

  Future<void> _loadMonth() async {
    final requestId = ++_monthRequestId;
    final requestedMonth = DateTime(_currentMonth.year, _currentMonth.month);
    setState(() {
      _loadingMonth = true;
      _monthError = null;
      _events = {};
    });
    try {
      final api = ref.read(apiClientProvider);
      final res = await api.get(
        '/api/calendar',
        queryParameters: {
          'year': _currentMonth.year,
          'month': _currentMonth.month,
        },
      );
      final list = (res.data['data'] as List?) ?? [];
      final events = <String, List<Map<String, dynamic>>>{};
      for (final item in list) {
        final map = item as Map<String, dynamic>;
        final types = (map['events'] as List?)?.cast<String>() ?? [];
        events[map['date'] as String] = types
            .map((type) => {'type': type})
            .toList();
      }
      if (!mounted ||
          requestId != _monthRequestId ||
          requestedMonth.year != _currentMonth.year ||
          requestedMonth.month != _currentMonth.month) {
        return;
      }
      setState(() {
        _events = events;
        _loadingMonth = false;
      });
      final selectedDate = _selectedDate;
      if (selectedDate != null) _selectDate(selectedDate, force: true);
    } catch (_) {
      if (!mounted || requestId != _monthRequestId) return;
      setState(() {
        _loadingMonth = false;
        _monthError = '本月计划加载失败';
        _loadingDay = false;
      });
    }
  }

  void _selectDate(DateTime d, {bool force = false}) {
    final key = _dateKey(d);
    final cached = _dayCache[key];
    setState(() {
      _selectedDate = d;
      _selectedDayData = cached;
      _dayError = null;
      _loadingDay = false;
    });
    if (cached != null && !force) return;
    final details = _events[key];
    if (details != null && details.isNotEmpty) {
      _loadDayDetail(d);
    } else {
      setState(() => _selectedDayData = _emptyDayData());
    }
  }

  Map<String, dynamic> _emptyDayData() => {
    'medications': <Map<String, dynamic>>[],
    'exercises': <Map<String, dynamic>>[],
    'followUps': <Map<String, dynamic>>[],
  };

  Future<void> _loadDayDetail(DateTime d) async {
    final requestId = ++_dayRequestId;
    final key = _dateKey(d);
    setState(() {
      _loadingDay = true;
      _dayError = null;
      _selectedDayData = null;
    });
    try {
      final api = ref.read(apiClientProvider);
      final res = await api.get(
        '/api/calendar/day',
        queryParameters: {'date': _dateKey(d)},
      );
      if (!mounted ||
          requestId != _dayRequestId ||
          _selectedDate == null ||
          _dateKey(_selectedDate!) != key) {
        return;
      }
      final data = res.data['data'] as Map<String, dynamic>? ?? _emptyDayData();
      _dayCache[key] = data;
      setState(() {
        _selectedDayData = data;
        _loadingDay = false;
      });
    } catch (_) {
      if (!mounted ||
          requestId != _dayRequestId ||
          _selectedDate == null ||
          _dateKey(_selectedDate!) != key) {
        return;
      }
      setState(() {
        _loadingDay = false;
        _dayError = '当天计划加载失败';
      });
    }
  }

  void _changeMonth(int offset) {
    final nextMonth = DateTime(
      _currentMonth.year,
      _currentMonth.month + offset,
    );
    final selectedDate = _selectedDate ?? _currentMonth;
    final nextDate = calendarDateForMonth(nextMonth, selectedDate);
    _dayRequestId++;
    setState(() {
      _currentMonth = nextMonth;
      _selectedDate = nextDate;
      _selectedDayData = null;
      _dayError = null;
      _loadingDay = false;
    });
    _loadMonth();
  }

  @override
  Widget build(BuildContext context) {
    return GradientScaffold(
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        surfaceTintColor: Colors.transparent,
        title: const Text(
          '健康日历',
          style: TextStyle(color: AppColors.textPrimary),
        ),
        leading: IconButton(
          icon: const Icon(Icons.arrow_back, color: AppColors.textPrimary),
          onPressed: () => popRoute(ref),
        ),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
            child: Container(
              padding: const EdgeInsets.fromLTRB(10, 8, 10, 12),
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: AppRadius.xlBorder,
              ),
              child: Column(
                children: [
                  _buildMonthHeader(),
                  _buildWeekdayHeader(),
                  const SizedBox(height: 6),
                  _buildCalendarGrid(),
                  if (_loadingMonth)
                    const Padding(
                      padding: EdgeInsets.fromLTRB(8, 8, 8, 0),
                      child: LinearProgressIndicator(
                        minHeight: 2,
                        color: _calendarColor,
                        backgroundColor: AppColors.calendarLight,
                      ),
                    ),
                  if (_monthError != null)
                    Padding(
                      padding: const EdgeInsets.only(top: 6),
                      child: TextButton.icon(
                        onPressed: _loadMonth,
                        icon: const Icon(Icons.refresh, size: 17),
                        label: Text('$_monthError，点击重试'),
                      ),
                    ),
                ],
              ),
            ),
          ),
          const SizedBox(height: 12),
          // 图例
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _Legend('用药提醒', _medColor),
                _Legend('运动计划', _exerciseColor),
                _Legend('复查随访', _followupColor),
              ],
            ),
          ),
          const SizedBox(height: 12),
          // 当日详情
          Expanded(child: _buildDayDetail()),
        ],
      ),
    );
  }

  Widget _buildMonthHeader() => Padding(
    padding: const EdgeInsets.symmetric(horizontal: 8),
    child: Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        IconButton(
          icon: const Icon(Icons.chevron_left),
          style: IconButton.styleFrom(
            backgroundColor: const Color(0xFFF8FAFC),
            foregroundColor: _calendarColor,
          ),
          onPressed: () {
            _changeMonth(-1);
          },
        ),
        Text(
          '${_currentMonth.year}年${_currentMonth.month}月',
          style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
        ),
        IconButton(
          icon: const Icon(Icons.chevron_right),
          style: IconButton.styleFrom(
            backgroundColor: const Color(0xFFF8FAFC),
            foregroundColor: _calendarColor,
          ),
          onPressed: () {
            _changeMonth(1);
          },
        ),
      ],
    ),
  );

  Widget _buildWeekdayHeader() {
    const wd = ['日', '一', '二', '三', '四', '五', '六'];
    return Row(
      children: wd
          .map(
            (d) => Expanded(
              child: Center(
                child: Text(
                  d,
                  style: const TextStyle(
                    fontSize: 14,
                    color: AppColors.textHint,
                  ),
                ),
              ),
            ),
          )
          .toList(),
    );
  }

  Widget _buildCalendarGrid() {
    final first = DateTime(_currentMonth.year, _currentMonth.month, 1);
    final last = DateTime(_currentMonth.year, _currentMonth.month + 1, 0);
    final dim = last.day;
    final sw = first.weekday % 7;
    final days = List.generate(42, (i) {
      final di = i - sw;
      return di < 0 || di >= dim ? null : di + 1;
    });
    final today = DateTime.now();

    return GridView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 7,
      ),
      itemCount: 42,
      itemBuilder: (_, i) {
        final day = days[i];
        if (day == null) return const SizedBox();
        final d = DateTime(_currentMonth.year, _currentMonth.month, day);
        final isToday = _dateKey(d) == _dateKey(today);
        final isSel =
            _selectedDate != null && _dateKey(d) == _dateKey(_selectedDate!);
        final evs = _events[_dateKey(d)] ?? [];

        return GestureDetector(
          onTap: () => _selectDate(d),
          child: Container(
            margin: const EdgeInsets.all(2),
            decoration: BoxDecoration(
              color: isSel
                  ? _calendarColor
                  : (isToday
                        ? _calendarColor.withValues(alpha: 0.08)
                        : Colors.white),
              borderRadius: AppRadius.mdBorder,
              border: isSel
                  ? Border.all(color: _calendarColor, width: 1.5)
                  : (isToday
                        ? Border.all(
                            color: _calendarColor.withValues(alpha: 0.24),
                            width: 1,
                          )
                        : null),
            ),
            child: Stack(
              alignment: Alignment.center,
              children: [
                Text(
                  '$day',
                  style: TextStyle(
                    fontSize: 16,
                    color: isSel ? Colors.white : AppColors.textPrimary,
                    fontWeight: FontWeight.w600,
                  ),
                ),
                if (evs.isNotEmpty)
                  Positioned(
                    bottom: 4,
                    child: Row(
                      children: evs
                          .take(3)
                          .map(
                            (e) => Container(
                              width: 5,
                              height: 5,
                              margin: const EdgeInsets.symmetric(horizontal: 1),
                              decoration: BoxDecoration(
                                color: _dotColor(e['type']?.toString() ?? ''),
                                shape: BoxShape.circle,
                              ),
                            ),
                          )
                          .toList(),
                    ),
                  ),
              ],
            ),
          ),
        );
      },
    );
  }

  Color _dotColor(String t) => switch (t) {
    'medication' => _medColor,
    'exercise' => _exerciseColor,
    'followup' => _followupColor,
    'health' => AppColors.health,
    'diet' => DietPalette.primary,
    'report' => AppColors.report,
    'device' => AppColors.device,
    _ => AppColors.textHint,
  };

  Widget _buildDayDetail() {
    if (_selectedDate == null) {
      return const Center(
        child: Text('点击日期查看当天安排', style: TextStyle(color: AppColors.textHint)),
      );
    }
    if (_loadingDay) {
      return const Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            SizedBox(
              width: 26,
              height: 26,
              child: CircularProgressIndicator(strokeWidth: 2.5),
            ),
            SizedBox(height: 12),
            Text('正在加载当天计划', style: TextStyle(color: AppColors.textSecondary)),
          ],
        ),
      );
    }
    if (_dayError != null) {
      return AppErrorState(
        title: _dayError!,
        subtitle: '请检查网络后重新加载',
        onRetry: () => _loadDayDetail(_selectedDate!),
      );
    }

    final data = _selectedDayData;
    if (data == null) {
      return const SizedBox.shrink();
    }

    final meds =
        (data['medications'] as List?)?.cast<Map<String, dynamic>>() ?? [];
    final exs =
        (data['exercises'] as List?)?.cast<Map<String, dynamic>>() ?? [];
    final fups =
        (data['followUps'] as List?)?.cast<Map<String, dynamic>>() ?? [];

    if (meds.isEmpty && exs.isEmpty && fups.isEmpty) {
      return AppEmptyState(
        icon: AppModuleVisuals.calendar.icon,
        iconColor: AppModuleVisuals.calendar.color,
        title: '${_selectedDate!.month}月${_selectedDate!.day}日没有健康安排',
        subtitle: '当天的用药、运动和复查计划会显示在这里',
      );
    }

    return ListView(
      padding: const EdgeInsets.fromLTRB(16, 0, 16, 24),
      children: [
        Text(
          '${_selectedDate!.month}月${_selectedDate!.day}日计划',
          style: AppTextStyles.sectionTitle,
        ),
        const SizedBox(height: 10),
        if (meds.isNotEmpty) ...[
          _PlanGroup(
            title: '用药提醒',
            color: _medColor,
            icon: AppModuleVisuals.medication.icon,
            items: meds
                .map((m) => _PlanItem(title: '${m['name'] ?? '未命名药品'}'))
                .toList(),
          ),
          const SizedBox(height: 12),
        ],
        if (exs.isNotEmpty) ...[
          _PlanGroup(
            title: '运动计划',
            color: _exerciseColor,
            icon: AppModuleVisuals.exercise.icon,
            items: exs
                .map(
                  (e) => _PlanItem(
                    title: '${e['type'] ?? '运动计划'}',
                    trailing: '${e['duration'] ?? 0}分钟',
                  ),
                )
                .toList(),
          ),
          const SizedBox(height: 12),
        ],
        if (fups.isNotEmpty) ...[
          _PlanGroup(
            title: '复查随访',
            color: _followupColor,
            icon: AppModuleVisuals.followup.icon,
            items: fups.map((f) {
              final doctor = '${f['doctorName'] ?? ''}'.trim();
              final department = '${f['department'] ?? ''}'.trim();
              final detail = [
                doctor,
                department,
              ].where((value) => value.isNotEmpty).join(' · ');
              return _PlanItem(
                title: '${f['title'] ?? '复查随访'}',
                trailing: detail,
              );
            }).toList(),
          ),
        ],
      ],
    );
  }
}

class _PlanItem {
  final String title;
  final String trailing;

  const _PlanItem({required this.title, this.trailing = ''});
}

class _PlanGroup extends StatelessWidget {
  final String title;
  final Color color;
  final IconData icon;
  final List<_PlanItem> items;

  const _PlanGroup({
    required this.title,
    required this.color,
    required this.icon,
    required this.items,
  });

  @override
  Widget build(BuildContext context) => Container(
    decoration: BoxDecoration(
      color: Colors.white,
      borderRadius: AppRadius.lgBorder,
    ),
    child: Column(
      children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(14, 13, 14, 8),
          child: Row(
            children: [
              Container(
                width: 32,
                height: 32,
                decoration: BoxDecoration(
                  color: color.withValues(alpha: 0.11),
                  borderRadius: AppRadius.smBorder,
                ),
                child: Icon(icon, size: 18, color: color),
              ),
              const SizedBox(width: 10),
              Text(
                title,
                style: const TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.w700,
                  color: AppColors.textPrimary,
                ),
              ),
            ],
          ),
        ),
        for (var index = 0; index < items.length; index++)
          _PlanRow(
            item: items[index],
            color: color,
            showDivider: index < items.length - 1,
          ),
      ],
    ),
  );
}

class _PlanRow extends StatelessWidget {
  final _PlanItem item;
  final Color color;
  final bool showDivider;

  const _PlanRow({
    required this.item,
    required this.color,
    required this.showDivider,
  });

  @override
  Widget build(BuildContext context) => Padding(
    padding: const EdgeInsets.only(left: 14),
    child: Row(
      children: [
        Container(
          width: 8,
          height: 8,
          decoration: BoxDecoration(
            color: color.withValues(alpha: 0.78),
            shape: BoxShape.circle,
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: Container(
            constraints: const BoxConstraints(minHeight: 48),
            decoration: BoxDecoration(
              border: showDivider
                  ? const Border(
                      bottom: BorderSide(color: AppColors.divider, width: 0.7),
                    )
                  : null,
            ),
            padding: const EdgeInsets.fromLTRB(0, 12, 14, 12),
            child: Row(
              children: [
                Expanded(
                  child: Text(
                    item.title,
                    style: const TextStyle(
                      fontSize: 15,
                      fontWeight: FontWeight.w600,
                      color: AppColors.textPrimary,
                    ),
                  ),
                ),
                if (item.trailing.isNotEmpty) ...[
                  const SizedBox(width: 12),
                  Flexible(
                    child: Text(
                      item.trailing,
                      textAlign: TextAlign.right,
                      style: const TextStyle(
                        fontSize: 13,
                        color: AppColors.textSecondary,
                      ),
                    ),
                  ),
                ],
              ],
            ),
          ),
        ),
      ],
    ),
  );
}

class _Legend extends StatelessWidget {
  final String label;
  final Color color;
  const _Legend(this.label, this.color);
  @override
  Widget build(BuildContext c) => Row(
    children: [
      Container(
        width: 8,
        height: 8,
        decoration: BoxDecoration(color: color, shape: BoxShape.circle),
      ),
      const SizedBox(width: 6),
      Text(
        label,
        style: const TextStyle(
          fontSize: 13,
          color: AppColors.textSecondary,
          fontWeight: FontWeight.w600,
        ),
      ),
    ],
  );
}

const Map<String, String> _extraStaticTextTitles = {
  'personalInfoList': '个人信息收集清单',
  'thirdPartySdkList': '第三方 SDK 共享清单',
};

const Map<String, String> _extraStaticTextContents = {
  'personalInfoList': '''## 个人信息收集清单

更新日期：2026年7月9日

本清单用于说明小脉健康 App 在当前功能范围内可能收集的个人信息类型、使用目的和使用场景。实际启用情况以正式部署配置和您实际使用的功能为准。

### 一、账号与登录信息

- 信息内容：手机号、登录状态、账号标识、验证码校验结果。
- 使用目的：完成注册、登录、身份识别、账号安全校验和账号注销。
- 使用场景：登录、注册、刷新登录状态、删除账号。

### 二、个人资料和健康档案

- 信息内容：姓名或昵称、性别、出生日期、头像、过敏史、慢病史、家族史、手术信息、饮食禁忌等。
- 使用目的：建立健康档案，辅助 AI 健康咨询、医生咨询、报告解读和健康管理。
- 使用场景：完善资料、健康档案、AI 对话、医生端查看。

### 三、健康数据

- 信息内容：血压、心率、血糖、血氧、体重、记录时间、数据来源、异常状态。
- 使用目的：记录和展示健康趋势，生成健康提醒，辅助 AI 分析和医生咨询。
- 使用场景：手动录入、蓝牙血压计同步、健康概览、通知提醒、AI 对话确认录入。

### 四、用药、运动、饮食和日历数据

- 信息内容：药品名称、剂量、频次、服药时间、打卡记录、运动计划、饮食记录、热量估算、日程提醒。
- 使用目的：提供用药管理、运动计划、饮食识别、健康提醒和日历管理。
- 使用场景：用药管理、服药打卡、运动计划、饮食拍照识别、通知中心。

### 五、报告、图片和文件信息

- 信息内容：您主动上传的检查报告图片、聊天图片、PDF 文件、AI 识别结果、报告摘要和审核状态。
- 使用目的：完成报告管理、报告解读、图片识别、AI 对话附件分析。
- 使用场景：上传报告、拍照识别、AI 聊天发送照片或 PDF。

### 六、蓝牙设备信息

- 信息内容：设备名称、设备标识、设备类型、服务 UUID、最近同步时间、短期去重记录。
- 使用目的：发现、绑定和同步支持的健康设备。
- 使用场景：蓝牙设备扫描、血压计绑定、血压数据同步。

### 七、设备、日志和网络信息

- 信息内容：设备型号、系统版本、App 版本、接口请求状态、错误信息、网络状态。
- 使用目的：功能适配、问题排查、服务安全、性能优化和合规审计。
- 使用场景：App 使用过程中的接口访问、异常处理、故障排查。
''',
  'thirdPartySdkList': '''## 第三方 SDK 共享清单

更新日期：2026年7月9日

本清单用于说明小脉健康 App 当前可能接入或依赖的第三方 SDK、外部接口和服务能力。正式上线前会根据实际启用的服务商、域名和资质信息继续更新。

### 一、AI 大模型和视觉识别服务

- 使用目的：AI 健康咨询、报告解读、图片识别、文本生成和内容理解。
- 可能共享的信息：您主动输入的对话内容、上传的图片或 PDF、健康档案摘要、需要 AI 分析的健康数据。
- 使用场景：AI 对话、拍照识别、上传报告、报告解读。
- 说明：实际服务商和接口地址以正式环境配置为准。

### 二、短信验证服务

- 使用目的：发送登录或注册验证码。
- 可能共享的信息：手机号、验证码发送状态、必要的风控信息。
- 使用场景：手机号登录、注册、身份验证。
- 说明：当前正式短信服务仍待接入，正式启用后补充服务商信息。

### 三、文件存储或服务器存储服务

- 使用目的：保存您主动上传的报告、图片和 PDF 文件。
- 可能共享的信息：文件内容、文件名称、上传时间、账号标识。
- 使用场景：报告管理、AI 聊天附件、图片识别。
- 说明：当前仍处于本地和开发环境阶段，正式服务器和域名确定后补充公网信息。

### 四、实时通信服务

- 使用目的：支持医生咨询消息、AI 流式回复和实时状态更新。
- 可能共享的信息：消息内容、会话标识、发送时间、账号标识。
- 使用场景：AI 对话、医生咨询、通知状态更新。

### 五、系统能力和开源组件

- 使用目的：蓝牙扫描、相机/相册选择、文件选择、图表展示、Markdown 渲染、网络请求等 App 基础能力。
- 可能涉及的信息：蓝牙设备信息、您主动选择的图片或文件、网络请求状态。
- 使用场景：蓝牙设备、上传照片、上传 PDF、健康趋势、合规文本展示。

我们不会向第三方出售您的个人信息。涉及个人信息处理的第三方服务，会在实现相关功能所必需的范围内使用，并在正式上线前结合实际服务商完善披露信息。
''',
};

String staticTextTitle(String type) => _extraStaticTextTitles[type] ?? '';

String staticTextContent(String type) => _extraStaticTextContents[type] ?? '';

/// 静态文本页
class StaticTextPage extends ConsumerWidget {
  final String type;
  const StaticTextPage({super.key, required this.type});
  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final titles = {
      'privacy': '隐私协议',
      'terms': '服务协议',
      'about': '关于小脉健康',
      ..._extraStaticTextTitles,
    };
    final contents = {
      'privacy': '''## 小脉健康隐私政策

更新日期：2026年6月29日
生效日期：2026年6月29日

小脉健康重视您的个人信息和健康数据保护。本政策说明我们在您使用小脉健康 App 及相关服务时，如何收集、使用、存储、共享和保护您的信息，以及您如何管理自己的信息。

请您在注册、登录和使用本应用前仔细阅读本政策。若您不同意本政策内容，请停止注册或使用相关服务。

### 一、我们收集的信息

为向您提供健康管理、AI 健康咨询、报告解读、饮食识别、用药管理、运动计划和蓝牙设备同步等功能，我们会根据您使用的具体功能收集以下信息：

1. 账号与身份信息
- 手机号码：用于注册、登录、身份识别和账号安全验证。
- 姓名或昵称、性别、出生日期、头像：用于完善个人资料和展示账号信息。
- 医生选择或签约关系：用于向您提供医生咨询、报告审核和随访相关服务。

2. 健康档案与健康记录
- 健康档案：诊断信息、手术类型、手术日期、过敏史、饮食禁忌、慢性病史、家族史等您主动填写的信息。
- 健康指标：血压、心率、血糖、血氧、体重、记录时间、记录来源等。
- 用药信息：药品名称、剂量、频次、服药时间、提醒设置、服药打卡记录等。
- 饮食记录：餐次、食物名称、份量、热量估算、饮食评分等。
- 运动计划：运动项目、计划内容、完成情况等。
- 检查报告：您上传的报告图片、报告文件、AI 识别出的指标、摘要、医生审核意见等。
- 在线咨询和随访信息：您与医生或系统产生的咨询内容、咨询状态、随访内容和时间。

3. 图片、相机、相册和文件信息
- 当您使用“拍照上传报告”“拍照识别饮食”“聊天中发送图片”等功能时，我们会调用设备相机，用于拍摄检查报告、饮食图片或您希望 AI 辅助查看的图片。
- 当您使用“从相册选择”“上传 PDF”等功能时，我们会读取您主动选择的图片或 PDF 文件，用于报告管理、饮食识别、AI 对话附件解析等功能。
- 未经您主动选择或确认，我们不会读取您的相册其他内容，也不会持续访问您的相机。

4. 蓝牙设备信息
- 当您使用蓝牙设备功能时，我们会扫描和连接附近支持的健康设备。目前实际支持以采用标准蓝牙血压服务的血压计为主；其他设备类型将在功能正式开放后另行说明。
- 我们会在本地保存已绑定设备的设备标识、设备名称、设备类型、服务 UUID、最近同步时间，以及用于避免重复同步的短期记录指纹。
- 通过血压计同步时，我们会读取并记录收缩压、舒张压、脉搏、测量时间等数据。

5. 位置信息相关说明
- Android 系统在部分版本中要求 App 申请定位权限后才允许进行蓝牙低功耗设备扫描。我们申请定位权限的目的，是用于发现和连接蓝牙健康设备。
- 当前代码未将您的定位经纬度上传至服务器，也未用于地图、轨迹、广告或位置画像。

6. 设备、日志和网络信息
- 设备型号、操作系统版本、App 版本、网络请求状态、接口错误信息等，用于功能适配、问题排查、服务安全和性能优化。
- 本应用会使用网络请求、流式消息、实时通信等能力，以便完成登录、AI 回复、医生咨询、数据同步等功能。

### 二、我们如何使用这些信息

我们会将收集的信息用于以下目的：

- 为您创建和维护账号，完成登录、退出登录、令牌刷新和账号安全验证。
- 记录和展示您的健康指标、用药、饮食、运动、报告、咨询和随访信息。
- 根据您输入或上传的信息，提供 AI 健康解释、报告预解读、饮食识别、用药和运动建议。
- 通过蓝牙连接健康设备并同步血压等测量数据。
- 向医生端展示与您咨询、签约、报告审核或随访相关的必要信息。
- 发送用药、运动、健康指标、报告处理等站内提醒。
- 进行系统维护、故障排查、安全审计、服务优化和合规管理。

本应用提供的 AI 分析、健康提醒和报告解读仅作为健康管理参考，不构成诊断、治疗或处方建议。如您出现胸痛、呼吸困难、意识异常、严重血压或血糖异常等紧急情况，请立即联系医生或前往医疗机构就诊。

### 三、权限调用说明

1. 相机权限
- 使用场景：拍摄检查报告、饮食图片、聊天附件图片。
- 使用目的：上传报告进行 AI 结构化解读；识别食物种类和估算份量、热量；在 AI 对话中让系统理解您主动发送的图片内容。

2. 相册/图片读取权限
- 使用场景：从相册选择报告图片、饮食图片或聊天图片。
- 使用目的：上传您主动选择的图片并完成报告管理、饮食识别或 AI 对话附件解析。

3. 文件读取权限
- 使用场景：在聊天中选择 PDF 文件。
- 使用目的：上传您主动选择的 PDF，供系统提取文字或生成 AI 对话上下文。

4. 蓝牙权限
- 使用场景：扫描、绑定和连接蓝牙健康设备。
- 使用目的：发现支持的血压计等健康设备，并同步血压、脉搏、测量时间等数据。

5. 定位权限
- 使用场景：Android 蓝牙低功耗扫描。
- 使用目的：满足系统蓝牙扫描要求。我们不会收集或上传您的精确定位经纬度。

### 四、信息上传和第三方服务

在您主动使用相关功能时，以下数据可能会上传至服务器：

- 账号信息、个人资料、健康档案和健康指标。
- 用药计划、服药记录、饮食记录、运动计划、日历和提醒数据。
- 检查报告图片、聊天图片、PDF 文件及其 AI 解析结果。
- 饮食图片识别结果、报告 AI 解读结果、AI 对话内容和医生咨询内容。
- 蓝牙血压计同步得到的血压、脉搏和测量时间。

为实现 AI 对话、图片识别、报告解读、知识库检索、短信验证和实时咨询等功能，我们可能接入以下第三方或外部服务。实际启用情况以正式部署配置为准：

- 大语言模型服务：用于 AI 健康咨询、报告预解读、文本生成和内容理解。
- 视觉模型服务：用于饮食图片识别、报告图片或聊天图片内容识别。
- 知识库检索服务：用于在 AI 回复时检索医学知识库或业务知识库资料。
- 短信服务：用于向您的手机号发送登录或注册验证码。
- 对象存储或服务器文件存储服务：用于保存您主动上传的报告、图片或 PDF 文件。
- 实时通信服务：用于医生咨询消息的实时收发。

我们不会向第三方出售您的个人信息或健康数据。若第三方服务处理您的信息，我们会尽力要求其仅在实现相关功能所必需的范围内处理，并采取合理的安全保护措施。

### 五、信息存储和保存期限

- 您的账号资料、健康档案、健康记录、用药、饮食、运动、报告、咨询、随访、通知和 AI 对话等业务数据，会保存至服务器数据库，用于持续向您提供服务。
- 您主动上传的检查报告图片会保存于服务器文件目录，并与报告记录关联；删除单份报告时，系统会删除对应报告记录和原始报告文件。
- 您在聊天中上传的图片或 PDF 会保存于服务器文件目录，用于 AI 附件解析和会话上下文展示。
- 饮食识别图片会临时保存至服务器用于识别处理，并提交视觉模型进行分析；识别结果会用于生成饮食记录。
- App 本地仅保存登录 token、偏好设置、已绑定蓝牙设备信息、最近同步信息和短期去重指纹等缓存数据，不作为健康业务数据的唯一来源。
- 我们将在实现服务目的所需期限内保存您的信息；法律法规另有要求的，从其规定。

### 六、您如何管理个人信息

您可以在 App 内查看、修改或删除部分信息：

- 在个人资料、健康档案、健康数据、用药、饮食、运动、报告、AI 会话等页面查看或管理相关记录。
- 在设置页查看《隐私政策》和《服务协议》。
- 在设置页使用“删除账号”功能申请注销账号。

账号注销后，系统会删除您的账号及主要业务数据，包括健康记录、用药记录、饮食记录、运动计划、报告记录、AI 对话、医生咨询、随访、通知、健康档案、登录令牌等。注销后相关数据不可恢复。

请注意：如法律法规要求留存，或为处理争议、审计、安全风控等确有必要，我们可能在法定或合理期限内保留必要记录。

### 七、我们如何保护信息安全

- 使用登录认证和访问控制保护您的账号和接口。
- 通过 HTTPS 等方式保护数据传输安全，正式上线时应使用有效的 HTTPS 证书。
- 对服务器、数据库和文件存储采取权限控制、日志审计、备份和安全配置。
- 医生端、管理端仅能访问其职责范围内必要的数据。
- 如发生个人信息安全事件，我们将按照法律法规要求及时采取补救措施并履行通知义务。

### 八、未成年人保护

本应用主要面向具备完全民事行为能力的成人用户。如未成年人需要使用本应用，应在监护人同意和指导下使用。监护人发现未成年人信息被不当收集或使用的，可通过本政策中的联系方式与我们联系。

### 九、政策更新

我们可能根据产品功能、法律法规或合规要求更新本政策。政策更新后，我们会在 App 内或相关页面展示更新后的版本；重大变更将通过弹窗、公告或其他合理方式提示您。

### 十、联系我们

如您对本政策、个人信息保护或账号注销有任何问题，可通过以下方式联系我们：

公司/运营主体：xxx
隐私负责人/客服邮箱：xxx
客服电话：xxx
联系地址：xxx''',
      'terms': '''## 小脉健康服务协议

更新日期：2026年6月29日
生效日期：2026年6月29日

欢迎使用小脉健康。请您在注册、登录和使用本应用前，仔细阅读并理解本服务协议。您点击同意、注册、登录或继续使用本应用，即表示您已阅读、理解并同意本协议及《隐私政策》。

如您不同意本协议，请停止注册或使用本应用。

### 一、服务内容

小脉健康为用户提供健康数据记录、健康档案管理、用药管理、饮食识别、运动计划、健康日历、检查报告管理、AI 健康解释和蓝牙健康设备同步等功能。具体服务内容会根据产品版本、实际开通情况、监管要求和运营安排进行调整。

本应用当前可能包含以下服务：

- 健康数据管理：记录和展示血压、心率、血糖、血氧、体重等指标。
- 蓝牙设备同步：连接支持的健康设备，当前主要用于血压计数据同步。
- 报告管理与预解读：上传检查报告图片，由系统进行结构化识别和 AI 辅助解读。
- 饮食识别：上传或拍摄饮食图片，识别食物种类、估算份量和热量。
- AI 健康咨询：根据您输入的文字、图片、PDF 或健康档案，提供健康解释和管理建议。
- 用药、运动和日历提醒：帮助您记录计划、查看进度和接收站内提醒。

### 二、账号注册与使用

- 您应使用真实、准确、有效的信息完成注册、登录和资料填写。
- 您应妥善保管账号、验证码、登录状态及设备，不得将账号转让、出租、出借或提供给他人使用。
- 因您主动泄露验证码、账号信息、设备信息或操作不当造成的损失，由您自行承担。
- 如我们发现您的账号存在违法违规、异常访问、攻击系统、冒用身份、恶意上传等行为，有权依法采取限制功能、暂停服务、要求整改、注销账号或配合监管处理等措施。

### 三、健康服务和医疗边界

小脉健康是健康管理辅助工具，不是医疗机构，不提供急诊服务。本应用中的 AI 分析、健康提醒、饮食运动建议、报告预解读、风险提示和自动生成内容，仅供健康管理参考，不构成诊断、治疗、处方、用药调整或医疗结论。

您理解并同意：

- AI 回复可能受输入信息完整性、图片清晰度、模型能力、网络状态和第三方服务状态影响，可能存在不准确、不完整或延迟。
- 检查报告解读仅是对报告文字、指标或图片内容的辅助说明，不能替代医生结合病史、体格检查、影像资料和线下检查作出的诊断。
- 饮食识别、热量估算、运动建议和健康评分可能存在误差，仅作为参考。
- 用药相关内容不应替代医生、药师或说明书建议。请勿根据 App 内容自行开始、停止、更换或调整药物。

如您出现胸痛、胸闷憋气、呼吸困难、意识异常、晕厥、言语不清、一侧肢体无力、严重头痛、严重过敏、血压或血糖明显异常等紧急情况，请立即拨打急救电话或前往医疗机构就诊，不应等待或依赖本应用回复。

### 四、用户上传内容和行为规范

您在使用本应用时，可能会上传文字、图片、PDF、检查报告、健康数据、咨询内容或其他资料。您应保证上传内容来源合法、真实、准确，不侵犯他人合法权益。

您不得利用本应用从事以下行为：

- 提交虚假、违法、侵权、辱骂、歧视、色情、暴力、诈骗或误导性信息。
- 冒用他人身份，上传他人个人信息、健康数据、病历、报告或图片。
- 干扰应用正常运行，攻击、破解、扫描、爬取或尝试未经授权访问系统、数据和接口。
- 利用本应用从事违法违规、损害他人权益、扰乱医疗服务秩序或违反公序良俗的行为。
- 将 AI 回复、报告解读或医生咨询内容用于违法用途，或断章取义传播造成误导。

因您上传内容不真实、不合法、不完整或侵犯他人权益造成的后果，由您自行承担。

### 五、第三方服务和外部能力

为实现短信登录、AI 对话、图片识别、报告解读、知识库检索、文件存储、实时通信等功能，本应用可能依赖第三方或外部服务。实际启用的服务以正式部署配置为准。

您理解并同意：

- 第三方服务可能因网络、系统维护、接口限制、模型能力、政策调整等原因导致延迟、中断、识别失败或结果不准确。
- 我们会尽力保障服务稳定性，但不承诺第三方服务始终无错误、不中断或满足您的全部预期。
- 涉及个人信息和健康数据处理的第三方服务，我们会按照《隐私政策》进行说明，并在合理范围内要求其采取安全保护措施。

### 六、数据与隐私

我们会按照《隐私政策》收集、使用、存储、共享和保护您的个人信息及健康数据。您可在 App 内“设置”中查看《隐私政策》，并依法行使查询、更正、删除和注销账号等权利。

您可以在 App 内删除部分健康数据、报告、对话或其他记录；您也可以通过“删除账号”功能申请注销账号。账号注销后，系统会删除您的账号及主要业务数据，包括健康记录、用药记录、饮食记录、运动计划、报告记录、AI 对话、医生咨询、随访、通知、健康档案、登录令牌等。注销后相关数据不可恢复。

如法律法规要求留存，或为处理争议、审计、安全风控等确有必要，我们可能在法定或合理期限内保留必要记录。

### 七、服务变更、中断与终止

为提升服务质量、修复问题、满足合规要求或调整业务安排，我们可能对功能、页面、规则、服务范围、第三方能力或收费策略进行调整。

在以下情况下，服务可能发生中断、延迟或终止：

- 系统维护、升级、迁移、故障修复或安全加固。
- 网络、服务器、数据库、短信、AI 模型、云服务或第三方接口异常。
- 您的设备、系统版本、权限设置、网络环境或操作方式导致服务无法正常使用。
- 法律法规、监管要求、司法行政机关要求或不可抗力。
- 您违反本协议、法律法规或平台规则。

我们会尽力降低服务中断对您的影响，但不承诺服务永久、连续、无错误运行。

### 八、知识产权

本应用的软件、页面、图标、文字、图片、交互设计、技术方案、数据结构、算法逻辑、商标、标识及相关内容，除依法属于第三方或用户自行上传的内容外，相关权利归小脉健康或其合法权利人所有。

未经授权，您不得复制、修改、传播、出租、出售、反向工程、反编译、抓取、镜像或以其他方式使用本应用及相关内容。

您上传的文字、图片、报告、PDF、健康数据等内容的合法权利仍归您或原权利人所有。为向您提供服务，您授权我们在必要范围内对相关内容进行存储、处理、识别、分析、展示和传输。

### 九、责任限制

在法律允许范围内，小脉健康不对以下情况造成的损失承担超出法定范围的责任：

- 因您提供的信息不真实、不完整、不准确或操作不当导致的结果偏差。
- 因您将 AI 回复、报告预解读、饮食识别、运动建议或健康提醒作为诊断、治疗、处方或急救依据造成的后果。
- 因第三方服务、网络故障、设备异常、系统维护、不可抗力或监管要求导致的服务中断、数据延迟或功能不可用。
- 因您泄露账号、验证码、设备或登录状态导致的信息泄露或损失。
- 因您上传违法、侵权或不当内容引发的争议或责任。

本协议不排除或限制法律法规规定不得排除或限制的责任。

### 十、协议更新

我们可能根据业务发展、产品变化、法律法规或监管要求更新本协议。更新后，我们会在 App 内或相关页面展示新版本；重大变更将通过弹窗、公告或其他合理方式提示您。

如您在协议更新后继续使用本应用，视为您已接受更新后的协议。

### 十一、法律适用与争议解决

本协议的订立、履行、解释和争议解决适用中华人民共和国法律。因本协议或本应用服务产生争议的，双方应先友好协商；协商不成的，依法向有管辖权的人民法院解决。

### 十二、联系我们

如您对本协议或服务使用有任何疑问，请通过以下方式联系我们：

公司/运营主体：xxx
客服/支持邮箱：xxx
客服电话：xxx
联系地址：xxx''',
      'about': '''## 关于小脉健康

版本：v1.0.0 (Build 20260101)

### 产品介绍
小脉健康是一款面向血管病患者的 AI 健康管理应用。以对话为核心交互方式，患者可以通过自然语言记录健康数据、获取饮食运动建议、管理用药、解读检查报告。

### 核心功能
- AI 智能问诊：基于大语言模型的健康咨询服务
- 健康数据管理：血压、心率、血糖、血氧、体重的记录与趋势分析
- 智能用药管理：AI 解析处方，自动生成用药计划和提醒
- 饮食识别分析：拍照即可识别食物种类、估算热量营养素
- 报告智能解读：上传检查报告，AI 自动提取指标并预解读
- 运动计划管理：制定和追踪每日运动目标
- 在线医生问诊：与签约医生进行远程咨询

### 开发团队
由专业医疗团队与 AI 技术团队联合打造。

### 技术支持
如遇到问题或有建议，请通过以下方式联系我们：
- 在线客服：App 内「设置」→「意见反馈」
- 客服热线：400-xxx-xxxx（工作日 9:00-18:00）

### 版权声明
© 2025-2026 小脉健康团队。保留所有权利。
本软件受中华人民共和国著作权法保护。''',
      ..._extraStaticTextContents,
    };
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        backgroundColor: Colors.transparent,
        elevation: 0,
        scrolledUnderElevation: 0,
        surfaceTintColor: Colors.transparent,
        leading: IconButton(
          icon: const Icon(Icons.chevron_left, color: AppColors.textPrimary),
          onPressed: () => popRoute(ref),
        ),
        title: Text(
          titles[type] ?? '',
          style: const TextStyle(
            fontSize: 19,
            color: AppColors.textPrimary,
            fontWeight: FontWeight.w600,
          ),
        ),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.fromLTRB(22, 18, 22, 36),
        child: Center(
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 680),
            child: MarkdownBody(
              data: contents[type] ?? '内容加载中...',
              selectable: true,
              styleSheet: MarkdownStyleSheet(
                textAlign: WrapAlignment.start,
                p: const TextStyle(
                  fontSize: 15,
                  height: 1.85,
                  fontWeight: FontWeight.w500,
                  color: AppColors.textPrimary,
                ),
                h1Align: WrapAlignment.center,
                h1Padding: const EdgeInsets.only(bottom: 18),
                h1: const TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.w700,
                  color: AppColors.textPrimary,
                  height: 1.35,
                  letterSpacing: 0,
                ),
                h2Padding: const EdgeInsets.only(top: 18, bottom: 8),
                h2: const TextStyle(
                  fontSize: 19,
                  fontWeight: FontWeight.w700,
                  color: AppColors.textPrimary,
                  height: 1.45,
                  letterSpacing: 0,
                ),
                h3Padding: const EdgeInsets.only(top: 16, bottom: 6),
                h3: const TextStyle(
                  fontSize: 17,
                  fontWeight: FontWeight.w600,
                  color: AppColors.textPrimary,
                  height: 1.45,
                  letterSpacing: 0,
                ),
                listBullet: const TextStyle(
                  fontSize: 15,
                  height: 1.85,
                  color: AppColors.textPrimary,
                ),
                blockSpacing: 10,
                listIndent: 22,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Widget _empty(BuildContext context, String title, String subtitle) => Center(
  child: Column(
    mainAxisSize: MainAxisSize.min,
    children: [
      Icon(Icons.inbox_outlined, size: 64, color: AppColors.textHint),
      const SizedBox(height: 12),
      Text(subtitle, style: Theme.of(context).textTheme.bodyMedium),
    ],
  ),
);
