import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../core/i18n/app_localizations.dart';
import '../../shared/widgets/action_bar.dart';
import '../../shared/widgets/form_section.dart';
import '../../shared/widgets/form_field_row.dart';
import '../../shared/widgets/nav_bar_config.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_colors_extension.dart';
import '../../core/constants/enums.dart';
import '../../core/data/mock_api_data.dart';

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

  @override
  ConsumerState<ExpenseApplicationApplyPage> createState() =>
      _ExpenseApplicationApplyPageState();
}

class _ExpenseApplicationApplyPageState
    extends ConsumerState<ExpenseApplicationApplyPage> {
  // ── 基本信息 ──
  String _urgency = Urgency.normal.value;
  final Set<String> _expenseTypes = {};
  bool _isTaxIncluded = false;
  final _purposeController = TextEditingController();
  String _validUntil = '';

  // ── 关联管控 ──
  String? _selectedProjectName;
  int? _selectedProjectId;
  String? _selectedSubjectName;
  int? _selectedSubjectId;
  double _availableBudget = 0;
  final _referenceNoController = TextEditingController();

  // ── 费用明细 ──
  final List<_DetailItem> _details = [];
  int _detailIdCounter = 1;

  // ── 附件 ──
  final List<String> _attachments = [];

  // ── 专用字段 ──
  String _estimatedStartDate = '';
  String _estimatedEndDate = '';
  bool _isOvernight = false;
  String? _transportType;
  String? _entertainmentTarget;
  String? _entertainmentLevel;
  int _guestCount = 1;
  int _companionCount = 0;
  String _entertainmentVenue = '';
  String _meetingStartDate = '';
  String _meetingEndDate = '';
  String _meetingVenue = '';

  @override
  void dispose() {
    _purposeController.dispose();
    _referenceNoController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final l10n = AppLocalizations.of(context);

    ref
        .read(navBarConfigProvider.notifier)
        .update(
          NavBarConfig(
            title: l10n.get('expenseApplyRequest'),
            showBack: true,
            onBack: () {
              if (_hasUnsaved()) {
                _showConfirmDialog(
                  l10n.get('confirmExit'),
                  l10n.get('unsavedContentWarning'),
                  l10n.get('continueEditing'),
                  l10n.get('discardAndExit'),
                  () => context.pop(),
                );
              } else {
                context.pop();
              }
            },
          ),
        );

    return PopScope(
      canPop: false,
      onPopInvokedWithResult: (didPop, _) {
        if (!didPop) {
          if (_hasUnsaved()) {
            _showConfirmDialog(
              l10n.get('confirmExit'),
              l10n.get('unsavedContentWarning'),
              l10n.get('continueEditing'),
              l10n.get('discardAndExit'),
              () => context.pop(),
            );
          } else {
            context.pop();
          }
        }
      },
      child: Column(
        children: [
          Expanded(
            child: GestureDetector(
              onTap: () => FocusScope.of(context).unfocus(),
              child: SingleChildScrollView(
                padding: const EdgeInsets.all(16),
                child: Column(
                  children: [
                    _buildBasicInfo(l10n),
                    _buildTypeSpecificFields(l10n),
                    _buildControlSection(l10n),
                    const SizedBox(height: 16),
                    _buildDetailsSection(l10n),
                    const SizedBox(height: 16),
                    _buildAttachmentSection(l10n),
                    const SizedBox(height: 80),
                  ],
                ),
              ),
            ),
          ),
          _buildBottomBar(l10n),
        ],
      ),
    );
  }

  // ═══ 1. 基本信息 ═══
  Widget _buildBasicInfo(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return FormSection(
      title: l10n.get('basicInfo'),
      leadingIcon: Icons.info_outline,
      children: [
        FormFieldRow(
          label: l10n.get('applicant'),
          value: '张三',
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('department'),
          value: '技术部',
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('date'),
          value: _today(),
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        _label(l10n.get('emergencyLevel'), required: true),
        const SizedBox(height: 8),
        _buildUrgencyRadio(l10n),
        const SizedBox(height: 16),
        _label(l10n.get('expenseType'), required: true),
        const SizedBox(height: 8),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: ExpenseType.values.map((opt) {
            final sel = _expenseTypes.contains(opt.value);
            return GestureDetector(
              onTap: () => setState(() {
                if (sel) {
                  _expenseTypes.remove(opt.value);
                } else {
                  _expenseTypes.add(opt.value);
                  final hints = {
                    'travel': 'hintTravelFields',
                    'entertainment': 'hintEntertainmentFields',
                    'meeting': 'hintMeetingFields',
                  };
                  final hintKey = hints[opt.value];
                  if (hintKey != null) {
                    TDMessage.showMessage(
                      context: context,
                      content: l10n.get(hintKey),
                      theme: MessageTheme.info,
                      icon: true,
                      marquee: MessageMarquee(speed: 3000, loop: 1, delay: 300),
                      duration: 3000,
                    );
                  }
                }
              }),
              child: TDTag(
                l10n.get(opt.labelKey),
                size: TDTagSize.large,
                theme: sel ? TDTagTheme.primary : TDTagTheme.defaultTheme,
                isOutline: !sel,
              ),
            );
          }).toList(),
        ),
        const SizedBox(height: 16),
        _label(l10n.get('feeReason'), required: true),
        const SizedBox(height: 8),
        TDTextarea(
          controller: _purposeController,
          hintText: l10n.get('enterFeeReason'),
          maxLines: 4,
          minLines: 1,
          maxLength: 500,
          indicator: true,
          padding: EdgeInsets.zero,
          bordered: true,
          backgroundColor: colors.bgPage,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('validUntil'),
          value: _validUntil,
          hint: l10n.get('pleaseSelect'),
          onTap: () => _pickDate((d) => setState(() => _validUntil = d)),
        ),
      ],
    );
  }

  Widget _buildUrgencyRadio(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return Row(
      children: Urgency.values.asMap().entries.map((e) {
        final sel = _urgency == e.value.value;
        final isCritical = e.value.value == Urgency.critical.value;
        final activeColor = isCritical ? colors.danger : colors.primary;
        return Padding(
          padding: EdgeInsets.only(right: e.key < 2 ? 24 : 0),
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: () => setState(() => _urgency = e.value.value),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Container(
                  width: 18,
                  height: 18,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    border: Border.all(
                      color: sel ? activeColor : colors.textPlaceholder,
                      width: 2,
                    ),
                  ),
                  child: sel
                      ? Center(
                          child: Container(
                            width: 8,
                            height: 8,
                            decoration: BoxDecoration(
                              shape: BoxShape.circle,
                              color: activeColor,
                            ),
                          ),
                        )
                      : null,
                ),
                const SizedBox(width: 6),
                Text(
                  l10n.get(e.value.labelKey),
                  style: TextStyle(
                    fontSize: AppFontSizes.subtitle,
                    color: sel ? activeColor : colors.textPrimary,
                  ),
                ),
              ],
            ),
          ),
        );
      }).toList(),
    );
  }

  // ═══ 2. 类型专用字段 ═══
  Widget _buildTypeSpecificFields(AppLocalizations l10n) {
    final ws = <Widget>[];
    if (_expenseTypes.contains('travel')) ws.add(_buildTravelFields(l10n));
    if (_expenseTypes.contains('entertainment')) {
      if (ws.isNotEmpty) ws.add(const SizedBox(height: 16));
      ws.add(_buildEntertainmentFields(l10n));
    }
    if (_expenseTypes.contains('meeting')) {
      if (ws.isNotEmpty) ws.add(const SizedBox(height: 16));
      ws.add(_buildMeetingFields(l10n));
    }
    if (ws.isEmpty) return const SizedBox(height: 16);
    return Column(
      children: [const SizedBox(height: 16), ...ws, const SizedBox(height: 16)],
    );
  }

  Widget _buildTravelFields(AppLocalizations l10n) {
    return FormSection(
      title: l10n.get('travelExpense'),
      leadingIcon: Icons.flight_outlined,
      children: [
        FormFieldRow(
          label: l10n.get('estimatedStartDate'),
          value: _estimatedStartDate,
          hint: l10n.get('pleaseSelect'),
          required: true,
          onTap: () =>
              _pickDate((d) => setState(() => _estimatedStartDate = d)),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('estimatedEndDate'),
          value: _estimatedEndDate,
          hint: l10n.get('pleaseSelect'),
          required: true,
          onTap: () => _pickDate((d) => setState(() => _estimatedEndDate = d)),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            _label(l10n.get('isOvernight')),
            TDSwitch(
              isOn: _isOvernight,
              onChanged: (v) {
                setState(() => _isOvernight = v);
                return true;
              },
            ),
          ],
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('transportType'),
          value: _transportType != null
              ? l10n.get(
                  TransportType.values
                      .firstWhere((t) => t.value == _transportType)
                      .labelKey,
                )
              : null,
          hint: l10n.get('pleaseSelect'),
          onTap: () => _showEnumPicker(
            l10n.get('selectTransport'),
            TransportType.values,
            (v) => setState(() => _transportType = v),
          ),
        ),
      ],
    );
  }

  Widget _buildEntertainmentFields(AppLocalizations l10n) {
    return FormSection(
      title: l10n.get('entertainmentExpense'),
      leadingIcon: Icons.people_outline,
      children: [
        FormFieldRow(
          label: l10n.get('entertainmentTargetUnit'),
          value: _entertainmentTarget,
          hint: l10n.get('pleaseEnter'),
          required: true,
          onTap: () => _showTextInput(
            l10n.get('entertainmentTargetUnit'),
            (v) => setState(() => _entertainmentTarget = v),
            initialText: _entertainmentTarget ?? '',
          ),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('entertainmentLevel'),
          value: _entertainmentLevel != null
              ? l10n.get(
                  EntertainmentLevel.values
                      .firstWhere((e) => e.value == _entertainmentLevel)
                      .labelKey,
                )
              : null,
          hint: l10n.get('pleaseSelect'),
          onTap: () => _showEnumPicker(
            l10n.get('selectEntertainmentLevel'),
            EntertainmentLevel.values,
            (v) => setState(() => _entertainmentLevel = v),
          ),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('externalCount'),
          value: '$_guestCount',
          onTap: () => _showNumberInput(
            l10n.get('externalCount'),
            (v) => setState(() => _guestCount = v),
            initialValue: _guestCount,
          ),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('internalCount'),
          value: '$_companionCount',
          onTap: () => _showNumberInput(
            l10n.get('internalCount'),
            (v) => setState(() => _companionCount = v),
            initialValue: _companionCount,
          ),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('venue'),
          value: _entertainmentVenue,
          hint: l10n.get('pleaseEnterLocation'),
          onTap: () => _showTextInput(
            l10n.get('venue'),
            (v) => setState(() => _entertainmentVenue = v),
            initialText: _entertainmentVenue,
          ),
        ),
      ],
    );
  }

  Widget _buildMeetingFields(AppLocalizations l10n) {
    return FormSection(
      title: l10n.get('meetingExpense'),
      leadingIcon: Icons.meeting_room_outlined,
      children: [
        FormFieldRow(
          label: l10n.get('estimatedStartDate'),
          value: _meetingStartDate,
          hint: l10n.get('pleaseSelect'),
          required: true,
          onTap: () => _pickDate((d) => setState(() => _meetingStartDate = d)),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('estimatedEndDate'),
          value: _meetingEndDate,
          hint: l10n.get('pleaseSelect'),
          required: true,
          onTap: () => _pickDate((d) => setState(() => _meetingEndDate = d)),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('venue'),
          value: _meetingVenue,
          hint: l10n.get('pleaseEnterMeetingLocation'),
          onTap: () => _showTextInput(
            l10n.get('meetingLocation'),
            (v) => setState(() => _meetingVenue = v),
            initialText: _meetingVenue,
          ),
        ),
      ],
    );
  }

  // ═══ 3. 关联管控 ═══
  Widget _buildControlSection(AppLocalizations l10n) {
    return FormSection(
      title: l10n.get('relatedControl'),
      leadingIcon: Icons.link_outlined,
      children: [
        FormFieldRow(
          label: l10n.get('relatedProject'),
          value: _selectedProjectName,
          hint: l10n.get('selectProject'),
          required: true,
          onTap: () {
            _showListPicker(
              l10n.get('selectProject'),
              mockProjects.map((p) => p.name).toList(),
              (v) {
                final p = mockProjects.firstWhere((x) => x.name == v);
                setState(() {
                  _selectedProjectId = p.id;
                  _selectedProjectName = p.name;
                  _selectedSubjectName = null;
                  _selectedSubjectId = null;
                  _availableBudget = 0;
                });
              },
            );
          },
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('budgetSubject'),
          value: _selectedSubjectName,
          hint: l10n.get('selectSubject'),
          required: true,
          onTap: _selectedProjectId != null
              ? () {
                  _showListPicker(
                    l10n.get('selectSubject'),
                    mockBudgetSubjects.map((s) => s.name).toList(),
                    (v) {
                      final s = mockBudgetSubjects.firstWhere(
                        (x) => x.name == v,
                      );
                      setState(() {
                        _selectedSubjectId = s.id;
                        _selectedSubjectName = s.name;
                        _availableBudget = getMockBudget(
                          _selectedProjectId!,
                          s.id,
                        );
                      });
                    },
                  );
                }
              : null,
        ),
        const SizedBox(height: 16),
        _buildBudgetRow(l10n),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('relatedContractNo'),
          value: _referenceNoController.text,
          hint: l10n.get('optional'),
          onTap: () => _showTextInput(
            l10n.get('relatedContractNo'),
            (v) => setState(() {
              _referenceNoController.text = v;
              _referenceNoController.selection = TextSelection.fromPosition(
                TextPosition(offset: v.length),
              );
            }),
            initialText: _referenceNoController.text,
          ),
        ),
      ],
    );
  }

  Widget _buildBudgetRow(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final over = _availableBudget <= 0;
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(
          l10n.get('availableBudget'),
          style: TextStyle(
            fontSize: AppFontSizes.subtitle,
            color: colors.textSecondary,
          ),
        ),
        Text(
          '¥${_availableBudget.toStringAsFixed(2)}',
          style: TextStyle(
            fontSize: AppFontSizes.subtitle,
            fontWeight: FontWeight.w700,
            color: over ? colors.danger : colors.amountPrimary,
          ),
        ),
      ],
    );
  }

  // ═══ 4. 费用明细 ═══
  Widget _buildDetailsSection(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return FormSection(
      title: l10n.get('expenseDetails'),
      leadingIcon: Icons.receipt_long_outlined,
      showAction: true,
      actionText: l10n.get('add'),
      onActionTap: _showDetailDialog,
      children: [
        Padding(
          padding: const EdgeInsets.only(bottom: 12),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              _label(l10n.get('isTaxIncluded')),
              TDSwitch(
                isOn: _isTaxIncluded,
                onChanged: (v) {
                  setState(() => _isTaxIncluded = v);
                  return true;
                },
              ),
            ],
          ),
        ),
        if (_details.isEmpty)
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 8),
            child: Text(
              l10n.get('noDetailHint'),
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                color: colors.textPlaceholder,
              ),
            ),
          )
        else
          ..._details.asMap().entries.map((e) {
            final d = e.value;
            return Container(
              margin: const EdgeInsets.symmetric(vertical: 8),
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: colors.bgPage,
                borderRadius: BorderRadius.circular(8),
              ),
              child: Row(
                children: [
                  Expanded(
                    flex: 3,
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          d.categoryName,
                          style: TextStyle(
                            fontSize: AppFontSizes.subtitle,
                            color: colors.textPrimary,
                          ),
                        ),
                        if (d.remark.isNotEmpty)
                          Text(
                            d.remark,
                            maxLines: 2,
                            overflow: TextOverflow.ellipsis,
                            style: TextStyle(
                              fontSize: AppFontSizes.caption,
                              color: colors.textSecondary,
                            ),
                          ),
                      ],
                    ),
                  ),
                  Text(
                    '${d.quantity}×¥${d.unitPrice.toStringAsFixed(2)}',
                    style: TextStyle(
                      fontSize: AppFontSizes.body,
                      color: colors.textSecondary,
                    ),
                  ),
                  const SizedBox(width: 8),
                  Text(
                    '¥${d.amount.toStringAsFixed(2)}',
                    style: TextStyle(
                      fontSize: AppFontSizes.subtitle,
                      fontWeight: FontWeight.w600,
                      color: colors.amountPrimary,
                    ),
                  ),
                  const SizedBox(width: 8),
                  GestureDetector(
                    onTap: () => setState(() => _details.removeAt(e.key)),
                    child: Container(
                      width: 24,
                      height: 24,
                      decoration: BoxDecoration(
                        color: colors.primaryLight,
                        shape: BoxShape.circle,
                      ),
                      child: Icon(
                        Icons.close,
                        size: 14,
                        color: colors.primary700,
                      ),
                    ),
                  ),
                ],
              ),
            );
          }),
        const SizedBox(height: 8),
        Container(
          height: 36,
          padding: const EdgeInsets.symmetric(vertical: 8),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Text(
                l10n.get('total'),
                style: TextStyle(
                  fontSize: AppFontSizes.body,
                  fontWeight: FontWeight.w600,
                  color: colors.textPrimary,
                ),
              ),
              Text(
                '¥${_totalAmount().toStringAsFixed(2)}',
                style: TextStyle(
                  fontSize: AppFontSizes.subtitle,
                  fontWeight: FontWeight.w700,
                  color: colors.amountPrimary,
                ),
              ),
            ],
          ),
        ),
        if (_totalAmount() > _availableBudget)
          Padding(
            padding: const EdgeInsets.only(top: 8),
            child: Row(
              children: [
                Icon(Icons.warning_amber, size: 14, color: colors.danger),
                const SizedBox(width: 6),
                Expanded(
                  child: Text(
                    l10n.get('overBudgetTriggerApproval'),
                    style: TextStyle(
                      fontSize: AppFontSizes.caption,
                      color: colors.danger,
                    ),
                  ),
                ),
              ],
            ),
          ),
      ],
    );
  }

  double _totalAmount() => _details.fold(0, (s, d) => s + d.amount);

  List<CostCategory> get _availableDetailCategories {
    if (_expenseTypes.isEmpty) return mockCostCategories;
    final codes = _expenseTypes
        .expand((et) => expenseTypeCategories[et] ?? <String>[])
        .toSet();
    return mockCostCategories.where((c) => codes.contains(c.code)).toList();
  }

  void _showDetailDialog() {
    _unfocus();
    final l10n = AppLocalizations.of(context);
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final cats = _availableDetailCategories;
    String cat = cats.isNotEmpty ? cats.first.code : 'other';
    String unit = l10n.get('unitPiece');
    String catLabel = l10n.get(cats.firstWhere((c) => c.code == cat).nameKey);
    String unitLabel = l10n.get('unitPiece');
    final qtyCtrl = TextEditingController(text: '1');
    final priceCtrl = TextEditingController();
    final remarkCtrl = TextEditingController();

    showModalBottomSheet(
      context: context,
      isScrollControlled: true,
      backgroundColor: colors.bgCard,
      shape: const RoundedRectangleBorder(
        borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
      ),
      builder: (ctx) => StatefulBuilder(
        builder: (ctx, setDlg) => Padding(
          padding: EdgeInsets.only(
            left: 16,
            right: 16,
            top: 16,
            bottom: 16 + MediaQuery.of(ctx).viewInsets.bottom,
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Center(
                child: Text(
                  l10n.get('addExpenseDetail'),
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.w600,
                    color: colors.textPrimary,
                  ),
                ),
              ),
              const SizedBox(height: 16),
              // 费用类别 — 左右布局，必填
              Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  SizedBox(
                    width: 80,
                    child: _label(l10n.get('expenseCategory'), required: true),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Container(
                      clipBehavior: Clip.antiAlias,
                      padding: const EdgeInsets.symmetric(horizontal: 12),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(color: colors.border, width: 1),
                      ),
                      child: TDDropdownMenu(
                        items: [
                          TDDropdownItem(
                            label: catLabel,
                            options: cats
                                .map(
                                  (c) => TDDropdownItemOption(
                                    value: c.code,
                                    label: l10n.get(c.nameKey),
                                  ),
                                )
                                .toList(),
                          ),
                        ],
                        closeOnClickOverlay: true,
                        onMenuClosed: (index) {
                          if (index >= 0 && index < cats.length) {
                            cat = cats[index].code;
                            setDlg(() {});
                          }
                        },
                      ),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 12),
              // 数量 — 左右布局，必填
              Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  SizedBox(
                    width: 80,
                    child: _label(l10n.get('quantity'), required: true),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Container(
                      clipBehavior: Clip.antiAlias,
                      padding: const EdgeInsets.symmetric(horizontal: 12),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(color: colors.border, width: 1),
                      ),
                      child: TDInput(
                        controller: qtyCtrl,
                        hintText: '>0',
                        contentAlignment: TextAlign.center,
                      ),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 12),
              // 单位 — 左右布局
              Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  SizedBox(width: 80, child: _label(l10n.get('unit'))),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Container(
                      clipBehavior: Clip.antiAlias,
                      padding: const EdgeInsets.symmetric(horizontal: 12),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(color: colors.border, width: 1),
                      ),
                      child: TDDropdownMenu(
                        items: [
                          TDDropdownItem(
                            label: unitLabel,
                            options: unitOptions
                                .map(
                                  (u) => TDDropdownItemOption(
                                    value: u,
                                    label: l10n.get(u),
                                  ),
                                )
                                .toList(),
                          ),
                        ],
                        closeOnClickOverlay: true,
                        onMenuClosed: (index) {
                          if (index >= 0 && index < unitOptions.length) {
                            unit = l10n.get(unitOptions[index]);
                            setDlg(() {});
                          }
                        },
                      ),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 12),
              // 单价 — 左右布局，必填
              Row(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: [
                  SizedBox(
                    width: 80,
                    child: _label(l10n.get('unitPrice'), required: true),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: Container(
                      clipBehavior: Clip.antiAlias,
                      padding: const EdgeInsets.symmetric(horizontal: 12),
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(8),
                        border: Border.all(color: colors.border, width: 1),
                      ),
                      child: TDInput(
                        controller: priceCtrl,
                        hintText: '>0',
                        contentAlignment: TextAlign.center,
                      ),
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 12),
              _label(l10n.get('detailRemark')),
              const SizedBox(height: 8),
              TDTextarea(
                controller: remarkCtrl,
                hintText: l10n.get('optional'),
                maxLines: 3,
                minLines: 1,
                maxLength: 200,
                indicator: true,
                padding: EdgeInsets.zero,
                bordered: true,
                backgroundColor: colors.bgPage,
              ),
              const SizedBox(height: 16),
              Row(
                children: [
                  Expanded(
                    child: TDButton(
                      text: l10n.get('cancel'),
                      size: TDButtonSize.large,
                      type: TDButtonType.outline,
                      shape: TDButtonShape.rectangle,
                      theme: TDButtonTheme.defaultTheme,
                      onTap: () => Navigator.pop(ctx),
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: TDButton(
                      text: l10n.get('confirm'),
                      size: TDButtonSize.large,
                      type: TDButtonType.fill,
                      shape: TDButtonShape.rectangle,
                      theme: TDButtonTheme.primary,
                      onTap: () {
                        final q = int.tryParse(qtyCtrl.text) ?? 0;
                        final p = double.tryParse(priceCtrl.text) ?? 0;
                        if (q <= 0 || p <= 0) {
                          TDToast.showText(
                            l10n.get('quantityPricePositive'),
                            context: context,
                          );
                          return;
                        }
                        setState(
                          () => _details.add(
                            _DetailItem(
                              id: _detailIdCounter++,
                              category: cat,
                              categoryName: l10n.get(
                                cats.firstWhere((c) => c.code == cat).nameKey,
                              ),
                              quantity: q,
                              unit: unit,
                              unitPrice: p,
                              amount: q * p.toDouble(),
                              remark: remarkCtrl.text,
                            ),
                          ),
                        );
                        Navigator.pop(ctx);
                      },
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  // ═══ 5. 附件上传 ═══
  Widget _buildAttachmentSection(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return FormSection(
      title: l10n.get('attachmentUpload'),
      leadingIcon: Icons.attach_file_outlined,
      children: [
        Text(
          l10n.get('maxAttachment'),
          style: TextStyle(
            fontSize: AppFontSizes.caption,
            color: colors.textPlaceholder,
          ),
        ),
        const SizedBox(height: 8),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ..._attachments.asMap().entries.map(
              (e) => Stack(
                clipBehavior: Clip.none,
                children: [
                  Container(
                    width: 80,
                    height: 80,
                    decoration: BoxDecoration(
                      color: colors.primaryLight,
                      borderRadius: BorderRadius.circular(4),
                    ),
                    child: Center(
                      child: Icon(Icons.image, color: colors.primary, size: 32),
                    ),
                  ),
                  Positioned(
                    right: -4,
                    top: -4,
                    child: GestureDetector(
                      onTap: () => setState(() => _attachments.removeAt(e.key)),
                      child: Container(
                        width: 20,
                        height: 20,
                        decoration: BoxDecoration(
                          color: colors.danger,
                          shape: BoxShape.circle,
                        ),
                        child: const Icon(
                          Icons.close,
                          size: 12,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            if (_attachments.length < 9)
              GestureDetector(
                onTap: () {
                  // Mock: add attachment (支持图片/PDF/Word/Excel)
                  final exts = ['.jpg', '.png', '.pdf', '.docx', '.xlsx'];
                  setState(
                    () => _attachments.add(
                      '附件_${DateTime.now().millisecondsSinceEpoch}${exts[_attachments.length % exts.length]}',
                    ),
                  );
                  TDToast.showText(
                    l10n.get('mockAttachmentAdded'),
                    context: context,
                  );
                },
                child: Container(
                  width: 80,
                  height: 80,
                  decoration: BoxDecoration(
                    color: colors.bgPage,
                    borderRadius: BorderRadius.circular(4),
                    border: Border.all(color: colors.border, width: 1),
                  ),
                  child: Center(
                    child: Icon(
                      Icons.add,
                      size: 24,
                      color: colors.textPlaceholder,
                    ),
                  ),
                ),
              ),
          ],
        ),
      ],
    );
  }

  // ═══ 6. 底部操作栏 ═══
  Widget _buildBottomBar(AppLocalizations l10n) {
    final isDraft = widget.id != null;
    return ActionBar(
      leftLabel: isDraft ? l10n.get('reset') : null,
      centerLabel: l10n.get('saveDraft'),
      rightLabel: l10n.get('submitApproval'),
      showLeft: isDraft,
      onLeftTap: isDraft
          ? () => _showConfirmDialog(
              l10n.get('confirmReset'),
              l10n.get('resetWarning'),
              l10n.get('cancel'),
              l10n.get('confirmReset'),
              _resetAll,
            )
          : null,
      onCenterTap: () {
        TDToast.showSuccess(l10n.get('draftSavedToast'), context: context);
        context.pop();
      },
      onRightTap: () {
        final err = _validate(l10n);
        if (err.isNotEmpty) {
          TDToast.showText(err.first, context: context);
          return;
        }
        TDToast.showSuccess(
          l10n.get('submittedAwaitingApproval'),
          context: context,
        );
        context.pop();
      },
    );
  }

  List<String> _validate(AppLocalizations l10n) {
    final e = <String>[];
    if (_expenseTypes.isEmpty) e.add(l10n.get('selectAtLeastOneExpenseType'));
    if (_purposeController.text.trim().isEmpty) {
      e.add(l10n.get('enterFeeReason'));
    }
    if (_selectedProjectId == null) e.add(l10n.get('selectProject'));
    if (_selectedSubjectId == null) e.add(l10n.get('selectSubject'));
    if (_details.isEmpty) e.add(l10n.get('addAtLeastOneDetail'));
    if (_expenseTypes.contains('travel')) {
      if (_estimatedStartDate.isEmpty) {
        e.add(l10n.get('selectEstimatedStartDate'));
      }
      if (_estimatedEndDate.isEmpty) e.add(l10n.get('selectEstimatedEndDate'));
    }
    if (_expenseTypes.contains('entertainment')) {
      if (_entertainmentTarget == null || _entertainmentTarget!.isEmpty) {
        e.add(l10n.get('entertainmentTargetUnit'));
      }
      if (_companionCount > _guestCount) {
        e.add(l10n.get('companionNotExceedGuest'));
      }
    }
    if (_expenseTypes.contains('meeting')) {
      if (_meetingStartDate.isEmpty) {
        e.add(l10n.get('selectEstimatedStartDate'));
      }
      if (_meetingEndDate.isEmpty) e.add(l10n.get('selectEstimatedEndDate'));
    }
    return e;
  }

  void _resetAll() => setState(() {
    _purposeController.clear();
    _expenseTypes.clear();
    _urgency = Urgency.normal.value;
    _isTaxIncluded = false;
    _validUntil = '';
    _selectedProjectId = null;
    _selectedProjectName = null;
    _selectedSubjectId = null;
    _selectedSubjectName = null;
    _availableBudget = 0;
    _referenceNoController.clear();
    _details.clear();
    _attachments.clear();
    _estimatedStartDate = '';
    _estimatedEndDate = '';
    _isOvernight = false;
    _transportType = null;
    _entertainmentTarget = null;
    _entertainmentLevel = null;
    _guestCount = 1;
    _companionCount = 0;
    _entertainmentVenue = '';
    _meetingStartDate = '';
    _meetingEndDate = '';
    _meetingVenue = '';
  });

  bool _hasUnsaved() =>
      _purposeController.text.isNotEmpty ||
      _expenseTypes.isNotEmpty ||
      _details.isNotEmpty ||
      _attachments.isNotEmpty ||
      _selectedProjectId != null ||
      _estimatedStartDate.isNotEmpty ||
      _estimatedEndDate.isNotEmpty ||
      _entertainmentTarget != null ||
      _meetingStartDate.isNotEmpty;

  void _unfocus() => FocusScope.of(context).unfocus();

  // ═══ 通用弹窗方法 ═══
  void _showConfirmDialog(
    String title,
    String content,
    String leftText,
    String rightText,
    VoidCallback onConfirm,
  ) {
    _unfocus();
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    showDialog(
      context: context,
      builder: (ctx) => TDAlertDialog(
        title: title,
        content: content,
        leftBtn: TDDialogButtonOptions(
          title: leftText,
          titleColor: colors.primary,
          action: () => Navigator.pop(ctx),
        ),
        rightBtn: TDDialogButtonOptions(
          title: rightText,
          titleColor: colors.danger,
          action: () {
            Navigator.pop(ctx);
            onConfirm();
          },
        ),
      ),
    );
  }

  void _showEnumPicker(
    String title,
    List<EnumEntry> entries,
    Function(String) onPick,
  ) {
    _showListPicker(
      title,
      entries.map((e) => AppLocalizations.of(context).get(e.labelKey)).toList(),
      (label) {
        final entry = entries.firstWhere(
          (e) => AppLocalizations.of(context).get(e.labelKey) == label,
        );
        onPick(entry.value);
      },
    );
  }

  void _showListPicker(
    String title,
    List<String> items,
    Function(String) onPick,
  ) {
    _unfocus();
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    TDPicker.showMultiPicker(
      context,
      title: title,
      backgroundColor: colors.bgCard,
      data: [items],
      onConfirm: (selected) {
        if (selected.isNotEmpty && selected[0] is int) {
          final idx = selected[0] as int;
          if (idx >= 0 && idx < items.length) {
            Navigator.of(context).pop();
            onPick(items[idx]);
          }
        }
      },
    );
  }

  void _showTextInput(
    String title,
    Function(String) onConfirm, {
    String initialText = '',
  }) {
    _unfocus();
    final l10n = AppLocalizations.of(context);
    final c = TextEditingController(text: initialText);
    showGeneralDialog(
      context: context,
      pageBuilder: (ctx, animation, secondaryAnimation) => TDInputDialog(
        textEditingController: c,
        title: title,
        hintText: l10n.get('pleaseEnter'),

        leftBtn: TDDialogButtonOptions(
          title: l10n.get('cancel'),
          action: () => Navigator.pop(ctx),
        ),
        rightBtn: TDDialogButtonOptions(
          title: l10n.get('confirm'),
          action: () {
            onConfirm(c.text);
            Navigator.pop(ctx);
          },
        ),
      ),
    );
  }

  void _showNumberInput(
    String title,
    Function(int) onConfirm, {
    int initialValue = 0,
  }) {
    _unfocus();
    final l10n = AppLocalizations.of(context);
    final c = TextEditingController(
      text: initialValue > 0 ? '$initialValue' : '',
    );
    showGeneralDialog(
      context: context,
      pageBuilder: (ctx, animation, secondaryAnimation) => TDInputDialog(
        textEditingController: c,
        title: title,
        hintText: l10n.get('enterNumber'),

        leftBtn: TDDialogButtonOptions(
          title: l10n.get('cancel'),
          action: () => Navigator.pop(ctx),
        ),
        rightBtn: TDDialogButtonOptions(
          title: l10n.get('confirm'),
          action: () {
            onConfirm(int.tryParse(c.text) ?? 0);
            Navigator.pop(ctx);
          },
        ),
      ),
    );
  }

  void _pickDate(Function(String) onPick) {
    _unfocus();
    final l10n = AppLocalizations.of(context);
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final theme = Theme.of(context);
    final now = DateTime.now();
    showModalBottomSheet(
      context: context,
      backgroundColor: Colors.transparent,
      builder: (ctx) => Theme(
        data: theme,
        child: TDDatePicker(
          title: l10n.get('selectDate'),
          backgroundColor: colors.bgCard,
          model: DatePickerModel(
            useYear: true,
            useMonth: true,
            useDay: true,
            useHour: false,
            useMinute: false,
            useSecond: false,
            useWeekDay: false,
            dateStart: [2020, 1, 1],
            dateEnd: [now.year + 1, 12, 31],
            dateInitial: [now.year, now.month, now.day],
          ),
          onConfirm: (selected) {
            onPick(
              '${selected['year']}-${selected['month']!.toString().padLeft(2, '0')}-${selected['day']!.toString().padLeft(2, '0')}',
            );
            Navigator.of(ctx).pop();
          },
          onCancel: (_) => Navigator.of(ctx).pop(),
        ),
      ),
    );
  }

  Widget _label(String t, {bool required = false}) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return Text.rich(
      TextSpan(
        children: [
          TextSpan(
            text: t,
            style: TextStyle(
              fontSize: AppFontSizes.subtitle,
              color: colors.textSecondary,
            ),
          ),
          if (required)
            TextSpan(
              text: ' *',
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                color: colors.danger,
              ),
            ),
        ],
      ),
    );
  }

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

class _DetailItem {
  final int id;
  final String category;
  final String categoryName;
  final int quantity;
  final String unit;
  final double unitPrice;
  final double amount;
  final String remark;
  const _DetailItem({
    required this.id,
    required this.category,
    required this.categoryName,
    required this.quantity,
    required this.unit,
    required this.unitPrice,
    required this.amount,
    required this.remark,
  });
}
