import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import '../../../core/i18n/app_localizations.dart';
import '../../../core/theme/app_colors.dart';
import '../../../core/theme/app_colors_extension.dart';
import '../../../core/data/mock_api_data.dart';
import '../expense_api.dart';
import '../../../shared/widgets/attachment_picker.dart';

/// 报销明细输入数据。
class ExpenseDetailInputData {
  final String category;
  final String categoryName;
  final String acctSubjectId;
  final String acctSubjectName;
  final String purpose;
  final double amount; // 含税金额
  final double taxRate;
  final String projectId;
  final String projectName;
  final String costDeptId;
  final String costDeptName;
  final String customerVendorId;
  final String customerVendorName;
  final double approvedAmount;
  final double offsetAmount;
  final String bankName;
  final String bankAccountName;
  final String bankAccount;
  final String remark;
  final List<String> attachmentPaths;
  final String sqMan;
  final String sqManName;
  final String aeNo;
  final String aeDd;

  const ExpenseDetailInputData({
    required this.category,
    required this.categoryName,
    required this.acctSubjectId,
    required this.acctSubjectName,
    required this.purpose,
    required this.amount,
    required this.taxRate,
    this.projectId = '',
    this.projectName = '',
    this.costDeptId = '',
    this.costDeptName = '',
    this.customerVendorId = '',
    this.customerVendorName = '',
    this.approvedAmount = 0.0,
    this.offsetAmount = 0.0,
    this.bankName = '',
    this.bankAccountName = '',
    this.bankAccount = '',
    this.remark = '',
    this.attachmentPaths = const [],
    this.sqMan = '',
    this.sqManName = '',
    this.aeNo = '',
    this.aeDd = '',
  });
}

/// 报销明细编辑弹窗。
///
/// 使用 [TDSlidePopupRoute] 从底部滑出，卡片化展示表单字段。
/// 参照 ExpenseApplyCreatePage 的 ExpenseDetailDialog 样式。
class ExpenseDetailDialog extends StatefulWidget {
  final List<CostCategory> categories;
  final List<Project> projects;
  final List<CostDept> costDepts;
  final List<CustomerVendor> customers;
  final List<EmployeeItem> employees;
  final AppLocalizations l10n;
  final ExpenseDetailInputData? initialData;
  final Future<bool> Function()? checkAttachHealth;

  const ExpenseDetailDialog({
    super.key,
    required this.categories,
    required this.projects,
    required this.costDepts,
    required this.customers,
    required this.employees,
    required this.l10n,
    this.initialData,
    this.checkAttachHealth,
  });

  /// 显示弹窗，返回 [ExpenseDetailInputData] 或 `null`（取消时）。
  static Future<ExpenseDetailInputData?> show(
    BuildContext context, {
    required List<CostCategory> categories,
    required List<Project> projects,
    required List<CostDept> costDepts,
    required List<CustomerVendor> customers,
    required List<EmployeeItem> employees,
    required AppLocalizations l10n,
    ExpenseDetailInputData? initialData,
    Future<bool> Function()? checkAttachHealth,
  }) {
    FocusScope.of(context).unfocus();
    return Navigator.push<ExpenseDetailInputData>(
      context,
      TDSlidePopupRoute<ExpenseDetailInputData>(
        slideTransitionFrom: SlideTransitionFrom.bottom,
        isDismissible: true,
        builder: (_) => ExpenseDetailDialog(
          categories: categories,
          projects: projects,
          costDepts: costDepts,
          customers: customers,
          employees: employees,
          l10n: l10n,
          initialData: initialData,
          checkAttachHealth: checkAttachHealth,
        ),
      ),
    );
  }

  @override
  State<ExpenseDetailDialog> createState() =>
      _ExpenseDetailDialogState();
}

class _ExpenseDetailDialogState extends State<ExpenseDetailDialog> {
  late String _cat;
  late TextEditingController _descCtrl;
  late TextEditingController _amountCtrl;
  CustomerVendor? _selCustomer;
  late TextEditingController _approvedAmountCtrl;
  late TextEditingController _remarkCtrl;
  late TextEditingController _bankNameCtrl;
  late TextEditingController _bankAccountNameCtrl;
  late TextEditingController _bankAccountCtrl;
  double _taxRate = 0;
  Project? _selProject;
  CostDept? _selDept;
  EmployeeItem? _selEmployee;
  late final AttachmentPickerController _attachmentCtrl;
  final ScrollController _scrollCtrl = ScrollController();
  bool _attachAvailable = false;

  static const _taxOptions = [0.0, 0.06, 0.09, 0.13];
  static const _taxLabels = ['0%', '6%', '9%', '13%'];

  List<CostCategory> get _cats => widget.categories;
  AppLocalizations get _l10n => widget.l10n;

  CostCategory get _selCat => _cats.firstWhere((c) => c.code == _cat);

  bool get _isEdit => widget.initialData != null;

  @override
  void initState() {
    super.initState();
    final d = widget.initialData;
    _cat = d != null
        ? (_cats.any((c) => c.code == d.category) ? d.category : _cats.first.code)
        : _cats.isNotEmpty ? _cats.first.code : 'other';
    _descCtrl = TextEditingController(text: d?.purpose ?? '');
    _amountCtrl = TextEditingController(text: d != null && d.amount > 0 ? d.amount.toStringAsFixed(2) : '');
    if (d != null && d.customerVendorName.isNotEmpty) {
      _selCustomer = CustomerVendor(id: '', name: d.customerVendorName);
    }
    _approvedAmountCtrl = TextEditingController(text: d != null && d.approvedAmount > 0 ? d.approvedAmount.toStringAsFixed(2) : '');
    _remarkCtrl = TextEditingController(text: d?.remark ?? '');
    _bankNameCtrl = TextEditingController(text: d?.bankName ?? '');
    _bankAccountNameCtrl = TextEditingController(text: d?.bankAccountName ?? '');
    _bankAccountCtrl = TextEditingController(text: d?.bankAccount ?? '');
    _taxRate = d?.taxRate ?? 0;
    if (d != null && d.sqMan.isNotEmpty && widget.employees.isNotEmpty) {
      final idx = widget.employees.indexWhere((e) => e.salNo == d.sqMan);
      if (idx >= 0) _selEmployee = widget.employees[idx];
    }
    if (d != null) {
      if (d.projectId.isNotEmpty && widget.projects.isNotEmpty) {
        _selProject = widget.projects.firstWhere((p) => p.id.toString() == d.projectId, orElse: () => widget.projects.first);
      }
      if (d.costDeptId.isNotEmpty && widget.costDepts.isNotEmpty) {
        _selDept = widget.costDepts.firstWhere((dept) => dept.id == d.costDeptId, orElse: () => widget.costDepts.first);
      }
      if (d.attachmentPaths.isNotEmpty) {
        // Restore attachments will be handled after build
        WidgetsBinding.instance.addPostFrameCallback((_) {
          _attachmentCtrl.restoreFromPaths(d.attachmentPaths);
        });
      }
    }
    _attachmentCtrl = AttachmentPickerController(maxCount: 9)
      ..addListener(() => setState(() {}));
    _checkAttachHealth();
  }

  Future<void> _checkAttachHealth() async {
    if (widget.checkAttachHealth == null) return;
    if (mounted) setState(() => _attachAvailable = false);
    try {
      final ok = await widget.checkAttachHealth!();
      if (mounted) setState(() => _attachAvailable = ok);
    } catch (_) {
      if (mounted) setState(() => _attachAvailable = false);
    }
  }

  @override
  void dispose() {
    _descCtrl.dispose();
    _amountCtrl.dispose();
    _approvedAmountCtrl.dispose();
    _remarkCtrl.dispose();
    _bankNameCtrl.dispose();
    _bankAccountNameCtrl.dispose();
    _bankAccountCtrl.dispose();
    _attachmentCtrl.dispose();
    _scrollCtrl.dispose();
    super.dispose();
  }

  void _confirm() {
    final amount = double.tryParse(_amountCtrl.text) ?? 0;
    if (amount <= 0) {
      TDToast.showText(_l10n.get('amountPositive'), context: context);
      return;
    }
    Navigator.pop(
      context,
      ExpenseDetailInputData(
        category: _cat,
        categoryName: _l10n.get(_selCat.nameKey),
        acctSubjectId: _selCat.acctSubjectId,
        acctSubjectName: _selCat.acctSubjectName,
        purpose: '',
        amount: amount,
        taxRate: _taxRate,
        projectId: _selProject?.id.toString() ?? '',
        projectName: _selProject?.name ?? '',
        costDeptId: _selDept?.id ?? '',
        costDeptName: _selDept?.name ?? '',
        customerVendorId: _selCustomer?.id ?? '',
        customerVendorName: _selCustomer?.name ?? '',
        approvedAmount: double.tryParse(_approvedAmountCtrl.text) ?? 0,
        bankName: _bankNameCtrl.text.trim(),
        bankAccountName: _bankAccountNameCtrl.text.trim(),
        bankAccount: _bankAccountCtrl.text.trim(),
        remark: _remarkCtrl.text.trim(),
        attachmentPaths: _attachmentCtrl.toPathList(),
        sqMan: _selEmployee?.salNo ?? '',
        sqManName: _selEmployee?.name ?? '',
        aeNo: widget.initialData?.aeNo ?? '',
        aeDd: widget.initialData?.aeDd ?? '',
      ),
    );
  }

  double get _amountExclTax => _taxRate > 0
      ? (double.tryParse(_amountCtrl.text) ?? 0) / (1 + _taxRate)
      : (double.tryParse(_amountCtrl.text) ?? 0);
  double get _taxAmount =>
      (double.tryParse(_amountCtrl.text) ?? 0) - _amountExclTax;

  @override
  Widget build(BuildContext context) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;

    return SafeArea(
      child: ConstrainedBox(
        constraints: BoxConstraints(
          maxHeight: MediaQuery.of(context).size.height * 0.8,
        ),
        child: Container(
          decoration: BoxDecoration(
            color: colors.bgPage,
            borderRadius:
                const BorderRadius.vertical(top: Radius.circular(16)),
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              _buildHeader(colors),
              Flexible(
                child: GestureDetector(
                onTap: () => FocusScope.of(context).unfocus(),
                behavior: HitTestBehavior.translucent,
                child: SingleChildScrollView(
                  controller: _scrollCtrl,
                  keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
                  padding: const EdgeInsets.fromLTRB(16, 0, 16, 12),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                      if (_isEdit && widget.initialData!.aeNo.isNotEmpty) ...[
                        _buildAeInfoCard(colors),
                        const SizedBox(height: 12),
                      ],
                      _buildCategoryCard(colors),
                      const SizedBox(height: 12),
                      _buildAcctSubjectCard(colors),
                      const SizedBox(height: 12),
                      _buildAmountCard(),
                      const SizedBox(height: 12),
                      _buildTaxRateCard(colors),
                      if ((double.tryParse(_amountCtrl.text) ?? 0) > 0) ...[
                        const SizedBox(height: 12),
                        _buildCalcInfo(colors),
                      ],
                      const SizedBox(height: 12),
                      _buildApprovedAmountCard(),
                      const SizedBox(height: 12),
                      _buildProjectCard(colors),
                      const SizedBox(height: 12),
                      _buildCostDeptCard(colors),
                      const SizedBox(height: 12),
                      _buildCustomerCard(colors),
                      const SizedBox(height: 12),
                      _buildEmployeeCard(colors),
                      const SizedBox(height: 12),
                      _buildBankInfoCard(colors),
                      const SizedBox(height: 12),
                      _buildRemarkInput(colors),
                      const SizedBox(height: 12),
                      _buildAttachmentCard(colors),
                    ],
                  ),
                ),
              ),
              ),
              Container(
                padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
                decoration: BoxDecoration(
                  color: colors.bgCard,
                  border: Border(
                    top: BorderSide(color: colors.border, width: 0.5),
                  ),
                ),
                child: _buildActions(),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // ── 标题栏 ──
  Widget _buildHeader(AppColorsExtension colors) {
    return Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Center(
          child: Container(
            margin: const EdgeInsets.only(top: 8, bottom: 4),
            width: 36,
            height: 4,
            decoration: BoxDecoration(
              color: colors.border,
              borderRadius: BorderRadius.circular(2),
            ),
          ),
        ),
        Padding(
          padding: const EdgeInsets.fromLTRB(20, 8, 12, 16),
          child: Row(
            children: [
              const SizedBox(width: 28),
              Expanded(
                child: Center(
                  child: Text(
                    _l10n.get('addExpenseDetail'),
                    style: TextStyle(
                      fontSize: AppFontSizes.title,
                      fontWeight: FontWeight.w600,
                      color: colors.textPrimary,
                    ),
                  ),
                ),
              ),
              GestureDetector(
                onTap: () => Navigator.pop(context),
                child: Padding(
                  padding: const EdgeInsets.all(4),
                  child: Icon(
                    Icons.close,
                    size: 20,
                    color: colors.textSecondary,
                  ),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }

  // ── picker 卡片 ──
  Widget _pickerCard({
    required String label,
    required bool required,
    required String currentLabel,
    required List<String> labels,
    required ValueChanged<int> onSelected,
    required AppColorsExtension colors,
    VoidCallback? onClear,
  }) {
    final tdTheme = TDTheme.of(context);
    final hasValue = onClear != null;
    return GestureDetector(
      onTap: () {
        if (labels.isEmpty) {
          TDToast.showText(_l10n.get('noData'), context: context);
          return;
        }
        TDPicker.showMultiPicker(
          context,
          title: label,
          backgroundColor: colors.bgCard,
          data: [labels],
          onConfirm: (selected) {
            if (selected.isNotEmpty && selected[0] is int) {
              final idx = selected[0] as int;
              if (idx >= 0 && idx < labels.length) {
                Navigator.of(context).pop();
                onSelected(idx);
              }
            }
          },
        );
      },
      child: Container(
        padding: const EdgeInsets.only(left: 16, right: 10, top: 12, bottom: 12),
        decoration: BoxDecoration(
          color: tdTheme.bgColorContainer,
          borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
          border: Border.all(color: tdTheme.componentStrokeColor),
        ),
        child: Row(
          children: [
            TDText(label, maxLines: 1, overflow: TextOverflow.visible, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, style: const TextStyle(letterSpacing: 0)),
            if (required)
              Padding(padding: const EdgeInsets.only(left: 4), child: TDText('*', font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, style: TextStyle(color: tdTheme.errorColor6))),
            const SizedBox(width: 12),
            Expanded(
              child: Row(mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.max, children: [
                Flexible(child: TDText(currentLabel, maxLines: 1, overflow: TextOverflow.ellipsis, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, textColor: tdTheme.textColorPrimary, textAlign: TextAlign.end)),
                const SizedBox(width: 4),
                SizedBox(
                  width: 18, height: 18,
                  child: hasValue
                      ? GestureDetector(onTap: onClear, child: Icon(Icons.close, size: 18, color: tdTheme.textColorPlaceholder))
                      : Icon(Icons.chevron_right, size: 18, color: tdTheme.textColorPlaceholder),
                ),
              ]),
            ),
          ],
        ),
      ),
    );
  }

  // ── 费用类别 ──
  Widget _buildCategoryCard(AppColorsExtension colors) {
    return _pickerCard(
      label: _l10n.get('expenseCategory'),
      required: true,
      currentLabel: '${_selCat.code}/${_l10n.get(_selCat.nameKey)}',
      labels: _cats.map((c) => '${c.code}/${_l10n.get(c.nameKey)}').toList(),
      colors: colors,
      onSelected: (idx) => setState(() => _cat = _cats[idx].code),
    );
  }

  // ── 输入卡片（对齐 pickerCard 样式） ──
  Widget _inputCard({
    required String label,
    required bool required,
    required TextEditingController controller,
    required String hintText,
    required AppColorsExtension colors,
    TextInputType? keyboardType,
    List<TextInputFormatter>? inputFormatters,
  }) {
    final tdTheme = TDTheme.of(context);
    final hasValue = controller.text.isNotEmpty;
    return Container(
      padding: const EdgeInsets.only(left: 16, right: 10, top: 12, bottom: 12),
      decoration: BoxDecoration(
        color: tdTheme.bgColorContainer,
        borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
        border: Border.all(color: tdTheme.componentStrokeColor),
      ),
      child: Row(
        children: [
          TDText(label, maxLines: 1, overflow: TextOverflow.visible, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, style: const TextStyle(letterSpacing: 0)),
          if (required)
            Padding(padding: const EdgeInsets.only(left: 4), child: TDText('*', font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, style: TextStyle(color: tdTheme.errorColor6))),
          const SizedBox(width: 12),
          Expanded(
            child: Row(mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.max, children: [
              Flexible(
                child: TextField(
                  controller: controller,
                  textAlign: TextAlign.end,
                  keyboardType: keyboardType,
                  inputFormatters: inputFormatters,
                  style: TextStyle(fontSize: 16, color: colors.textPrimary),
                  decoration: InputDecoration(
                    hintText: hintText,
                    hintStyle: TextStyle(fontSize: 16, color: colors.textPlaceholder),
                    border: InputBorder.none,
                    isDense: true,
                    contentPadding: EdgeInsets.zero,
                  ),
                  onChanged: (_) => setState(() {}),
                ),
              ),
              const SizedBox(width: 4),
              SizedBox(
                width: 18, height: 18,
                child: hasValue
                    ? GestureDetector(
                        onTap: () { controller.clear(); setState(() {}); },
                        child: Icon(Icons.close, size: 18, color: tdTheme.textColorPlaceholder),
                      )
                    : null,
              ),
            ]),
          ),
        ],
      ),
    );
  }

  // ── 含税金额 ──
  Widget _buildAmountCard() {
    return _inputCard(
      label: _l10n.get('amountInclTax'),
      required: true,
      controller: _amountCtrl,
      hintText: '>0',
      colors: Theme.of(context).extension<AppColorsExtension>()!,
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
      inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}$'))],
    );
  }

  // ── 税率 ──
  Widget _buildTaxRateCard(AppColorsExtension colors) {
    final currentLabel =
        '${(_taxRate * 100).toStringAsFixed(0)}%';
    return _pickerCard(
      label: _l10n.get('taxRate'),
      required: true,
      currentLabel: currentLabel,
      labels: _taxLabels.toList(),
      colors: colors,
      onSelected: (idx) => setState(() {
        _taxRate = _taxOptions[idx];
      }),
    );
  }

  // ── 计算信息 ──
  Widget _buildCalcInfo(AppColorsExtension colors) {
    final amount = double.tryParse(_amountCtrl.text) ?? 0;
    if (amount <= 0) return const SizedBox.shrink();
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
      decoration: BoxDecoration(
        color: colors.primaryLight,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        children: [
          Expanded(
            child: Text(
              '${_l10n.get('amountExcludingTax')}: ¥${_amountExclTax.toStringAsFixed(2)}',
              style: TextStyle(
                fontSize: AppFontSizes.body,
                color: colors.textSecondary,
              ),
            ),
          ),
          Text(
            '${_l10n.get('taxAmount')}: ¥${_taxAmount.toStringAsFixed(2)}',
            style: TextStyle(
              fontSize: AppFontSizes.body,
              color: colors.textSecondary,
            ),
          ),
        ],
      ),
    );
  }

  // ── 导入单据信息 ──
  Widget _buildAeInfoCard(AppColorsExtension colors) {
    final d = widget.initialData!;
    final tdTheme = TDTheme.of(context);
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: _cardDecoration(tdTheme),
      child: Column(
        children: [
          _readOnlyRow(tdTheme, _l10n.get('expenseApplyNo'), d.aeNo),
          const SizedBox(height: 8),
          _readOnlyRow(tdTheme, _l10n.get('applyDate'), d.aeDd),
        ],
      ),
    );
  }

  Widget _readOnlyRow(TDThemeData tdTheme, String label, String value) {
    return Row(children: [
      TDText(label, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, style: const TextStyle(letterSpacing: 0)),
      const SizedBox(width: 12),
      Expanded(child: TDText(value, maxLines: 1, overflow: TextOverflow.ellipsis, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, textColor: tdTheme.textColorPrimary, textAlign: TextAlign.end)),
    ]);
  }

  BoxDecoration _cardDecoration(TDThemeData tdTheme) => BoxDecoration(
    color: tdTheme.bgColorContainer,
    borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
    border: Border.all(color: tdTheme.componentStrokeColor),
  );

  // ── 申请人 ──
  Widget _buildEmployeeCard(AppColorsExtension colors) {
    final employees = widget.employees;
    return _pickerCard(
      label: _l10n.get('applicant'),
      required: false,
      currentLabel: _selEmployee != null ? '${_selEmployee!.salNo}/${_selEmployee!.name}' : _l10n.get('pleaseSelect'),
      labels: employees.map((e) => '${e.salNo}/${e.name}').toList(),
      colors: colors,
      onSelected: (idx) => setState(() {
        _selEmployee = employees[idx];
        _bankNameCtrl.text = _selEmployee!.bnkNo;
        _bankAccountNameCtrl.text = _selEmployee!.accName;
        _bankAccountCtrl.text = _selEmployee!.bnkId;
      }),
      onClear: _selEmployee != null ? () => setState(() {
        _selEmployee = null;
        _bankNameCtrl.clear();
        _bankAccountNameCtrl.clear();
        _bankAccountCtrl.clear();
      }) : null,
    );
  }

  // ── 客户/厂商 ──
  Widget _buildCustomerCard(AppColorsExtension colors) {
    final vendors = widget.customers;
    return _pickerCard(
      label: _l10n.get('customerVendor'),
      required: false,
      currentLabel: _selCustomer != null ? '${_selCustomer!.id}/${_selCustomer!.name}' : _l10n.get('pleaseSelect'),
      labels: vendors.map((v) => '${v.id}/${v.name}').toList(),
      colors: colors,
      onSelected: (idx) => setState(() => _selCustomer = vendors[idx]),
      onClear: _selCustomer != null ? () => setState(() => _selCustomer = null) : null,
    );
  }

  // ── 已充金额 ──
  Widget _buildApprovedAmountCard() {
    return _inputCard(
      label: _l10n.get('approvedAmount'),
      required: false,
      controller: _approvedAmountCtrl,
      hintText: '0',
      colors: Theme.of(context).extension<AppColorsExtension>()!,
      keyboardType: const TextInputType.numberWithOptions(decimal: true),
      inputFormatters: [FilteringTextInputFormatter.allow(RegExp(r'^\d*\.?\d{0,2}$'))],
    );
  }

  // ── 备注 ──
  Widget _buildRemarkInput(AppColorsExtension colors) {
    final tdTheme = TDTheme.of(context);
    return TDTextarea(
      controller: _remarkCtrl,
      label: _l10n.get('remark'),
      hintText: _l10n.get('enterRemark'),
      maxLines: 3,
      minLines: 1,
      maxLength: 500,
      indicator: true,
      decoration: BoxDecoration(
        color: tdTheme.bgColorContainer,
        borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
        border: Border.all(color: tdTheme.componentStrokeColor),
      ),
      onChanged: (_) => setState(() {}),
    );
  }

  // ── 操作按钮 ──
  Widget _buildAttachmentCard(AppColorsExtension colors) {
    final tdTheme = TDTheme.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Padding(
          padding: const EdgeInsets.only(left: 4),
          child: TDText(
            _l10n.get('attachmentUpload'),
            font: tdTheme.fontBodyLarge,
            fontWeight: FontWeight.w400,
            style: const TextStyle(letterSpacing: 0),
          ),
        ),
        const SizedBox(height: 4),
        if (!_attachAvailable)
          TDText(_l10n.get('attachServiceUnavailable'),
              font: tdTheme.fontBodyMedium, fontWeight: FontWeight.w400,
              textColor: tdTheme.textColorPlaceholder)
        else
          AttachmentPicker(
            controller: _attachmentCtrl,
            maxImageSizeMB: 10,
            maxFileSizeMB: 20,
            allowedExtensions: const ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
            onFileRejected: (file, reason) {
              if (context.mounted) TDToast.showText(reason, context: context);
            },
          ),
      ],
    );
  }

  // ── 会计科目（只读，选择类别后自动带出） ──
  Widget _buildAcctSubjectCard(AppColorsExtension colors) {
    return _readOnlyCard(
      label: _l10n.get('acctSubject'),
      value: '${_selCat.acctSubjectId}/${_selCat.acctSubjectName}',
      colors: colors,
    );
  }

  Widget _readOnlyCard({
    required String label,
    required String value,
    required AppColorsExtension colors,
  }) {
    final tdTheme = TDTheme.of(context);
    return Container(
      padding: const EdgeInsets.only(left: 16, right: 16, top: 12, bottom: 12),
      decoration: BoxDecoration(
        color: tdTheme.bgColorContainer,
        borderRadius: BorderRadius.circular(tdTheme.radiusDefault),
        border: Border.all(color: tdTheme.componentStrokeColor),
      ),
      child: Row(
        children: [
          TDText(label, maxLines: 1, overflow: TextOverflow.visible, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400, style: const TextStyle(letterSpacing: 0)),
          const SizedBox(width: 12),
          Expanded(
            child: TDText(value, maxLines: 1, overflow: TextOverflow.ellipsis, font: tdTheme.fontBodyLarge, fontWeight: FontWeight.w400,
              textColor: tdTheme.textColorPrimary, textAlign: TextAlign.end),
          ),
        ],
      ),
    );
  }

  // ── 关联项目 ──
  Widget _buildProjectCard(AppColorsExtension colors) {
    final projects = widget.projects;
    return _pickerCard(
      label: _l10n.get('relatedProject'),
      required: false,
      currentLabel: _selProject != null ? '${_selProject!.id}/${_selProject!.name}' : _l10n.get('pleaseSelect'),
      labels: projects.map((p) => '${p.id}/${p.name}').toList(),
      colors: colors,
      onSelected: (idx) => setState(() => _selProject = projects[idx]),
      onClear: _selProject != null ? () => setState(() => _selProject = null) : null,
    );
  }

  // ── 费用承担部门 ──
  Widget _buildCostDeptCard(AppColorsExtension colors) {
    final depts = widget.costDepts;
    return _pickerCard(
      label: _l10n.get('costDept'),
      required: false,
      currentLabel: _selDept != null ? '${_selDept!.id}/${_selDept!.name}' : _l10n.get('pleaseSelect'),
      labels: depts.map((d) => '${d.id}/${d.name}').toList(),
      colors: colors,
      onSelected: (idx) => setState(() => _selDept = depts[idx]),
      onClear: _selDept != null ? () => setState(() => _selDept = null) : null,
    );
  }

  // ── 收款银行信息 ──
  Widget _buildBankInfoCard(AppColorsExtension colors) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        _inputCard(label: _l10n.get('bankName'), required: false, controller: _bankNameCtrl, hintText: _l10n.get('pleaseEnter'), colors: colors),
        const SizedBox(height: 12),
        _inputCard(label: _l10n.get('bankAccountName'), required: false, controller: _bankAccountNameCtrl, hintText: _l10n.get('pleaseEnter'), colors: colors),
        const SizedBox(height: 12),
        _inputCard(label: _l10n.get('bankAccount'), required: false, controller: _bankAccountCtrl, hintText: _l10n.get('pleaseEnter'), colors: colors),
      ],
    );
  }

  Widget _buildActions() {
    return Row(
      children: [
        Expanded(
          child: TDButton(
            text: _l10n.get('cancel'),
            size: TDButtonSize.large,
            type: TDButtonType.outline,
            shape: TDButtonShape.rectangle,
            theme: TDButtonTheme.defaultTheme,
            onTap: () => Navigator.pop(context),
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: TDButton(
            text: _isEdit ? _l10n.get('confirmEdit') : _l10n.get('add'),
            size: TDButtonSize.large,
            type: TDButtonType.fill,
            shape: TDButtonShape.rectangle,
            theme: TDButtonTheme.primary,
            onTap: _confirm,
          ),
        ),
      ],
    );
  }
}
