import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:dio/dio.dart';
import 'package:go_router/go_router.dart';
import '../../core/network/api_exception.dart';
import '../../core/utils/responsive.dart';
import '../../core/utils/date_utils.dart' as du;
import '../../shared/widgets/form_section.dart';
import '../../shared/widgets/form_field_row.dart';
import '../../shared/widgets/app_skeletons.dart';
import '../../shared/widgets/nav_bar_config.dart';
import 'widgets/expense_detail_dialog.dart';
import '../../shared/widgets/searchable_picker_sheet.dart';
import '../../shared/widgets/loading_dialog.dart';
import '../../shared/widgets/action_bar.dart';
import 'expense_api.dart';
import '../../core/i18n/app_localizations.dart';
import 'expense_model.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_colors_extension.dart';
import '../../core/navigation/host_app_channel.dart';
import '../../core/utils/amount_utils.dart';

/// 费用报销修改页
///
/// 完全参考 [ExpenseCreatePage] 创建页，但：
/// - 无草稿功能
/// - 无附件上传区
/// - 无从申请单导入链接
/// - initState 中加载原单数据并回填
/// - 基本信息区顶部加单号、日期只读行
/// - ActionBar 仅含提交按钮
/// - 提交时 HeadData 加 BX_NO，成功 pop(true) 返回详情页
class ExpenseEditPage extends ConsumerStatefulWidget {
  final String billNo;
  const ExpenseEditPage({super.key, required this.billNo});

  @override
  ConsumerState<ExpenseEditPage> createState() => _ExpenseEditPageState();
}

class _ExpenseEditPageState extends ConsumerState<ExpenseEditPage> {
  final _purposeController = TextEditingController();
  final _purposeFocus = FocusNode();
  final _remarkController = TextEditingController();
  final _remarkFocus = FocusNode();
  final _scrollCtrl = ScrollController();
  final _detailsSectionKey = GlobalKey();

  // ── 参考数据（从 API 加载） ──
  List<CurrencyItem> _currencies = [];
  EmployeeItem? _selEmployee;
  dynamic _acctTree;
  bool _firstBuild = true;
  bool _refDataLoading = true;
  bool _addingDetail = false;
  bool _isSubmitting = false;
  bool _canEditApprovedAmount = false;
  String? _loadingError;

  // ── 原单数据 ──
  ExpenseModel? _expense;
  String _billNo = '';
  String _expenseDate = '';
  // ── 报销部门 ──
  String _selectedDeptId = '';
  String _selectedDeptName = '';

  @override
  void initState() {
    super.initState();
    SystemChrome.setSystemUIOverlayStyle(
      const SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.dark,
      ),
    );
    _purposeFocus.addListener(() => _ensureVisible(_purposeFocus));
    _remarkFocus.addListener(() => _ensureVisible(_remarkFocus));
    _billNo = widget.billNo;
    _loadData();
    _checkApprovedAmountRight();
  }

  Future<void> _checkApprovedAmountRight() async {
    try {
      final api = ref.read(expenseApiProvider);
      final ok = await api.checkSpcRight('MONCJ_AMTN_SH');
      if (mounted) setState(() => _canEditApprovedAmount = ok);
    } catch (_) {
      if (mounted) setState(() => _canEditApprovedAmount = false);
    }
  }

  Future<void> _loadData() async {
    try {
      final api = ref.read(expenseApiProvider);
      final results = await Future.wait([
        api.getCurrencies(),
        api.fetchDetail(widget.billNo),
        api.getAcctSubjects(),
      ]);
      if (!mounted) return;
      final expense = results[1] as ExpenseModel;
      setState(() {
        _currencies = results[0] as List<CurrencyItem>;
        _acctTree = _convertAcctTree(results[2]);
        _expense = expense;
        _expenseDate = expense.expenseDate != null
            ? du.DateUtils.formatDate(expense.expenseDate!)
            : _today();
        _selectedDeptId = expense.deptId;
        _selectedDeptName = expense.deptName;
        _selEmployee = expense.applicantId.isNotEmpty
            ? EmployeeItem(salNo: expense.applicantId, name: expense.applicantName)
            : null;
        _purposeController.text = expense.purpose;
        _remarkController.text = expense.remark;
        _refDataLoading = false;
        _firstBuild = false;
        _recalculateAmount();
      });
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _refDataLoading = false;
        _firstBuild = false;
        _loadingError = e.toString();
      });
    }
  }

  Future<void> _showEmployeePicker() async {
    FocusManager.instance.primaryFocus?.unfocus();
    final l10n = AppLocalizations.of(context);
    final api = ref.read(expenseApiProvider);
    final result = await showSearchablePicker<EmployeeItem>(
      context,
      title: '${l10n.get('select')}${l10n.get('expensePersonnel')}',
      searchHint: l10n.get('search'),
      loader: (keyword, page) =>
          api.getEmployees(keyword: keyword, page: page, size: 20),
      labelBuilder: (e) => e.name.isEmpty ? e.salNo : '${e.salNo} ${e.name}',
      onRefresh: () => api.clearRefCache(),
    );
    if (result != null && mounted) {
      setState(() => _selEmployee = result);
    }
  }

  void _ensureVisible(FocusNode node) {
    if (!node.hasFocus) return;
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (node.hasFocus && _scrollCtrl.hasClients) {
        final ctx = node.context;
        if (ctx != null) {
          Scrollable.ensureVisible(
            ctx,
            alignment: 0.3,
            duration: const Duration(milliseconds: 300),
          );
        }
      }
    });
  }

  @override
  void dispose() {
    _purposeController.dispose();
    _purposeFocus.dispose();
    _remarkController.dispose();
    _remarkFocus.dispose();
    _scrollCtrl.dispose();
    super.dispose();
  }

  // ═══ 状态更新助手（替代 Riverpod controller） ═══

  void _updatePurpose(String purpose) {
    if (_expense == null) return;
    setState(() {
      _expense = _expense!.copyWith(purpose: purpose);
    });
  }

  void _updateRemark(String remark) {
    if (_expense == null) return;
    setState(() {
      _expense = _expense!.copyWith(remark: remark);
    });
  }

  void _updatePaymentMethod(String method) {
    if (_expense == null) return;
    setState(() {
      _expense = _expense!.copyWith(paymentMethod: method);
    });
  }

  void _updateCurrencyCode(String code) {
    if (_expense == null) return;
    setState(() {
      _expense = _expense!.copyWith(currencyCode: code);
    });
  }

  void _setGenerateVoucher(bool value) {
    if (_expense == null) return;
    setState(() {
      _expense = _expense!.copyWith(isGenerateVoucher: value);
    });
  }

  void _addDetail(ExpenseDetailModel detail) {
    if (_expense == null) return;
    setState(() {
      _expense = _expense!.copyWith(details: [..._expense!.details, detail]);
    });
    _recalculateAmount();
  }

  void _updateDetail(int index, ExpenseDetailModel detail) {
    if (_expense == null) return;
    final details = [..._expense!.details];
    details[index] = detail;
    setState(() {
      _expense = _expense!.copyWith(details: details);
    });
    _recalculateAmount();
  }

  void _removeDetail(int index) {
    if (_expense == null) return;
    final details = [..._expense!.details]..removeAt(index);
    setState(() {
      _expense = _expense!.copyWith(details: details);
    });
    _recalculateAmount();
  }

  List<Map<String, dynamic>> _convertAcctTree(dynamic tree) {
    if (tree is! List) return [];
    return (tree as List).map<Map<String, dynamic>>((item) {
      final map = Map<String, dynamic>.from(item is Map ? item : {});
      if (map.containsKey('children') && map['children'] != null) {
        map['children'] = _convertAcctTree(map['children']);
      }
      return map;
    }).toList();
  }

  void _recalculateAmount() {
    if (_expense == null) return;
    var totalAmount = 0.0;
    var approvedAmount = 0.0;
    for (final d in _expense!.details) {
      totalAmount += d.totalAmount;
      approvedAmount += d.approvedAmount;
    }
    setState(() {
      _expense = _expense!.copyWith(
        totalAmount: totalAmount,
        approvedAmount: approvedAmount,
      );
    });
  }

  String _currencyLabel(String code) {
    final match = _currencies.where((c) => c.curId == code);
    return match.isNotEmpty ? '${match.first.curId}/${match.first.name}' : code;
  }

  Future<void> _showDeptPicker() async {
    FocusManager.instance.primaryFocus?.unfocus();
    final l10n = AppLocalizations.of(context);
    final api = ref.read(expenseApiProvider);
    final result = await showSearchablePicker<DepartmentItem>(
      context,
      title: '${l10n.get('select')}${l10n.get('expenseDept')}',
      searchHint: l10n.get('search'),
      loader: (keyword, page) =>
          api.getDepartments(keyword: keyword, page: page, size: 20),
      labelBuilder: (d) => d.name.isEmpty ? d.dep : '${d.dep} ${d.name}',
      onRefresh: () => api.clearRefCache(),
    );
    if (result != null && mounted) {
      setState(() {
        _selectedDeptId = result.dep;
        _selectedDeptName = result.name;
      });
    }
  }

  Map<String, dynamic> _buildSubmitData() {
    final expense = _expense!;
    return {
      'HeadData': {
        'BX_NO': _billNo,
        'BX_DD': _today(),
        'DEP': _selectedDeptId,
        'USR_NO': _selEmployee?.salNo ?? '',
        'PAY_ID': expense.paymentMethod,
        'USR': HostAppChannel.usr,
        'REM': expense.remark,
        'CUR_ID': expense.currencyCode,
        'EXC_RTO': 1,
        'REASON': expense.purpose,
        'VOH_ID': expense.isGenerateVoucher ? 'T' : 'F',
      },
      'BodyData1': expense.details.asMap().entries.map((e) {
        final d = e.value;
        final item = <String, dynamic>{
          'BX_NO': _billNo,
          'BX_DD': _today(),
          'ACC_NO': d.acctSubjectId,
          'AMT': d.totalAmount,
          'AMTN': d.amount,
          'AMTN_SH': d.approvedAmount,
          'REM': d.remark,
          'CUST': d.customerVendorId,
          'IDX_NO': d.expenseCategory,
          'OBJ_NO': d.projectId.isNotEmpty ? d.projectId : '',
          'TAX': d.taxAmount,
          'TAX_RTO': d.taxRate,
          'DEP': d.costDeptId,
          'AE_NO': d.aeNo,
          'AE_DD': d.aeDd,
          'BNK_NO': d.bankName,
          'BNK_ID': d.bankAccount,
          'ACCNAME': d.bankAccountName,
          'SQ_MAN': d.sqMan.isNotEmpty ? d.sqMan : HostAppChannel.usr,
          'EST_ITM': d.aeNo.isNotEmpty ? d.sortOrder : 0,
        };
        if (d.preItm != null) {
          item['PRE_ITM'] = d.preItm;
        }
        return item;
      }).toList(),
    };
  }

  // ═══ Build ═══

  @override
  Widget build(BuildContext context) {
    final r = ResponsiveHelper.of(context);
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final l10n = AppLocalizations.of(context);
    final bottomInset = MediaQuery.of(context).padding.bottom;

    if (_loadingError != null) {
      return Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.error_outline, size: 48, color: colors.danger),
            const SizedBox(height: 16),
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 32),
              child: Text(
                _loadingError!,
                textAlign: TextAlign.center,
                style: TextStyle(
                  fontSize: AppFontSizes.body,
                  color: colors.textSecondary,
                ),
              ),
            ),
            const SizedBox(height: 16),
            TDButton(
              text: l10n.get('retry'),
              size: TDButtonSize.medium,
              onTap: () {
                setState(() {
                  _loadingError = null;
                  _firstBuild = true;
                  _refDataLoading = true;
                });
                _loadData();
              },
            ),
          ],
        ),
      );
    }

    if (_firstBuild || _expense == null) {
      return const SkeletonFormPage(showImportLink: false);
    }

    Future.microtask(
      () => ref.read(pageBackProvider.notifier).state = () => _doPop(l10n),
    );

    Widget pageContent = PopScope(
      canPop: false,
      onPopInvokedWithResult: (didPop, _) {
        if (didPop) return;
        _doPop(l10n);
      },
      child: Column(
        children: [
          Expanded(
            child: Align(
              alignment: Alignment.topCenter,
              child: ConstrainedBox(
                constraints: BoxConstraints(maxWidth: r.formMaxWidth),
                child: SingleChildScrollView(
                  controller: _scrollCtrl,
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      _buildBasicInfoSection(l10n, colors),
                      const SizedBox(height: 16),
                      Container(
                        key: _detailsSectionKey,
                        child: _buildDetailSection(l10n, colors),
                      ),
                      const SizedBox(height: 24),
                      _buildPageFooter(),
                    ],
                  ),
                ),
              ),
            ),
          ),
          ColoredBox(
            color: colors.bgCard,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                _buildBottomButtons(l10n),
                if (bottomInset > 0) SizedBox(height: bottomInset),
              ],
            ),
          ),
        ],
      ),
    );

    return pageContent;
  }

  Widget _buildBasicInfoSection(
    AppLocalizations l10n,
    AppColorsExtension colors,
  ) {
    final expense = _expense!;
    return FormSection(
      title: l10n.get('basicInfo'),
      leadingIcon: Icons.info_outline,
      children: [
        FormFieldRow(
          label: l10n.get('expenseNo'),
          value: _billNo,
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('date'),
          value: _expenseDate,
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('expensePersonnel'),
          value: _selEmployee != null
              ? '${_selEmployee!.salNo}/${_selEmployee!.name}'
              : '',
          hint: l10n.get('pleaseSelect'),
          onTap: _showEmployeePicker,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('expenseDept'),
          value: _selectedDeptName.isNotEmpty
              ? '$_selectedDeptId/$_selectedDeptName'
              : null,
          hint: l10n.get('pleaseSelect'),
          onTap: _refDataLoading ? null : () => _showDeptPicker(),
        ),
        const SizedBox(height: 16),
        _label(l10n.get('expenseReason'), required: true),
        const SizedBox(height: 8),
        TDTextarea(
          controller: _purposeController,
          focusNode: _purposeFocus,
          hintText: l10n.get('enterExpenseReason'),
          maxLines: 4,
          minLines: 1,
          maxLength: 500,
          indicator: true,
          padding: EdgeInsets.zero,
          bordered: true,
          backgroundColor: colors.bgPage,
          onChanged: (_) => _updatePurpose(_purposeController.text),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('paymentMethod'),
          value: expense.paymentMethod,
          hint: l10n.get('pleaseEnter'),
          onTap: () => _showTextInput(
            l10n.get('paymentMethod'),
            (v) => _updatePaymentMethod(v),
            initialText: expense.paymentMethod,
          ),
          onClear: () => _updatePaymentMethod(''),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('currency'),
          value: expense.currencyCode.isNotEmpty
              ? _currencyLabel(expense.currencyCode)
              : null,
          hint: l10n.get('selectCurrency'),
          onTap: () => _showCurrencyPicker(expense.currencyCode),
          onClear: () => _updateCurrencyCode(''),
        ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              l10n.get('generateVoucher'),
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                color: colors.textSecondary,
              ),
            ),
            TDSwitch(
              isOn: expense.isGenerateVoucher,
              onChanged: (v) {
                _setGenerateVoucher(v);
                return v;
              },
            ),
          ],
        ),
        const SizedBox(height: 16),
        _label(l10n.get('remark')),
        const SizedBox(height: 8),
        TDTextarea(
          controller: _remarkController,
          focusNode: _remarkFocus,
          hintText: l10n.get('enterRemark'),
          maxLines: 3,
          minLines: 1,
          maxLength: 500,
          indicator: true,
          padding: EdgeInsets.zero,
          bordered: true,
          backgroundColor: colors.bgPage,
          onChanged: (_) => _updateRemark(_remarkController.text),
        ),
      ],
    );
  }

  Widget _buildDetailSection(AppLocalizations l10n, AppColorsExtension colors) {
    final expense = _expense!;
    final totalAmount = expense.details.fold<double>(
      0,
      (sum, d) => sum + d.totalAmount,
    );
    final totalApproved = expense.details.fold<double>(
      0,
      (sum, d) => sum + d.approvedAmount,
    );
    return FormSection(
      title: l10n.get('expenseDetails'),
      leadingIcon: Icons.receipt_long_outlined,
      showAction: true,
      actionText: l10n.get('add'),
      onActionTap: () => _showAddDetailDialog(),
      children: [
        if (expense.details.isEmpty)
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 8),
            child: Text(
              l10n.get('noDetailHint'),
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                color: colors.textPlaceholder,
              ),
            ),
          )
        else
          ...expense.details.asMap().entries.map((entry) {
            final d = entry.value;
            return GestureDetector(
              onTap: () => _showAddDetailDialog(editIndex: entry.key),
              child: Container(
                margin: const EdgeInsets.symmetric(vertical: 6),
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: colors.bgPage,
                  borderRadius: BorderRadius.circular(8),
                ),
                child: Row(
                  children: [
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Expanded(
                                child: Text(
                                  d.categoryName.isNotEmpty
                                      ? '${d.expenseCategory}/${d.categoryName}'
                                      : d.expenseCategory,
                                  style: TextStyle(
                                    fontSize: AppFontSizes.body,
                                    fontWeight: FontWeight.w500,
                                    color: colors.textPrimary,
                                  ),
                                ),
                              ),
                              Column(
                                crossAxisAlignment: CrossAxisAlignment.end,
                                children: [
                                  Text(
                                    formatAmount(d.totalAmount),
                                    style: TextStyle(
                                      fontSize: AppFontSizes.body,
                                      fontWeight: FontWeight.w600,
                                      color: colors.amountPrimary,
                                    ),
                                  ),
                                  if (d.approvedAmount > 0)
                                    Text(
                                      formatAmount(d.approvedAmount),
                                      style: TextStyle(
                                        fontSize: AppFontSizes.body,
                                        fontWeight: FontWeight.w600,
                                        color: colors.success,
                                      ),
                                    ),
                                ],
                              ),
                            ],
                          ),
                          const SizedBox(height: 2),
                          Text(
                            '${l10n.get('amountExcludingTax')}: ${formatAmount(d.amount)}',
                            style: TextStyle(
                              fontSize: AppFontSizes.caption,
                              color: colors.textSecondary,
                            ),
                          ),
                          if (d.taxAmount > 0)
                            Text(
                              '${l10n.get('taxAmount')}: ${formatAmount(d.taxAmount)}',
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.taxRate > 0)
                            Text(
                              '${l10n.get('taxRate')}: ${d.taxRate.toStringAsFixed(0)}%',
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.acctSubjectId.isNotEmpty)
                            Text(
                              '${l10n.get('acctSubject')}: ${d.acctSubjectId}${d.acctSubjectName.isNotEmpty ? '/${d.acctSubjectName}' : ''}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.aeNo.isNotEmpty)
                            Text(
                              '${l10n.get('expenseApplyNo')}: ${d.aeNo}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.aeDd.isNotEmpty)
                            Text(
                              '${l10n.get('applyDate')}: ${d.aeDd.length >= 10 ? d.aeDd.substring(0, 10) : d.aeDd}',
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.projectId.isNotEmpty)
                            Text(
                              '${l10n.get('project')}: ${d.projectId}${d.projectName.isNotEmpty ? '/${d.projectName}' : ''}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.costDeptId.isNotEmpty)
                            Text(
                              '${l10n.get('costDept')}: ${d.costDeptId}${d.costDeptName.isNotEmpty ? '/${d.costDeptName}' : ''}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.customerVendorId.isNotEmpty)
                            Text(
                              '${l10n.get('customerVendor')}: ${d.customerVendorId}${d.customerVendorName.isNotEmpty ? '/${d.customerVendorName}' : ''}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.sqMan.isNotEmpty)
                            Text(
                              '${l10n.get('applicant')}: ${d.sqMan}${d.sqManName.isNotEmpty ? '/${d.sqManName}' : ''}',
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.bankAccountName.isNotEmpty)
                            Text(
                              '${l10n.get('bankAccountName')}: ${d.bankAccountName}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.bankName.isNotEmpty)
                            Text(
                              '${l10n.get('bankName')}: ${d.bankName}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.bankAccount.isNotEmpty)
                            Text(
                              '${l10n.get('bankAccount')}: ${d.bankAccount}',
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                          if (d.remark.isNotEmpty)
                            Text(
                              '${l10n.get('remark')}: ${d.remark}',
                              maxLines: 2,
                              overflow: TextOverflow.ellipsis,
                              style: TextStyle(
                                fontSize: AppFontSizes.caption,
                                color: colors.textSecondary,
                              ),
                            ),
                        ],
                      ),
                    ),
                    const SizedBox(width: 8),
                    GestureDetector(
                      onTap: () => _removeDetail(entry.key),
                      child: Icon(
                        Icons.close,
                        size: 18,
                        color: colors.textSecondary,
                      ),
                    ),
                  ],
                ),
              ),
            );
          }),
        const SizedBox(height: 8),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              l10n.get('totalExpense'),
              style: TextStyle(
                fontSize: AppFontSizes.body,
                fontWeight: FontWeight.w600,
                color: colors.textPrimary,
              ),
            ),
            Text(
              formatAmount(totalAmount),
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                fontWeight: FontWeight.w700,
                color: colors.amountPrimary,
              ),
            ),
          ],
        ),
        const SizedBox(height: 4),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              l10n.get('approvedTotal'),
              style: TextStyle(
                fontSize: AppFontSizes.body,
                fontWeight: FontWeight.w600,
                color: colors.textPrimary,
              ),
            ),
            Text(
              formatAmount(totalApproved),
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                fontWeight: FontWeight.w700,
                color: totalApproved > 0 ? colors.success : colors.textPrimary,
              ),
            ),
          ],
        ),
      ],
    );
  }

  Future<void> _showAddDetailDialog({int? editIndex}) async {
    if (_addingDetail) return;
    _addingDetail = true;
    try {
      final l10n = AppLocalizations.of(context);
      final expense = _expense!;
      ExpenseDetailInputData? initialData;
      if (editIndex != null) {
        final d = expense.details[editIndex];
        initialData = ExpenseDetailInputData(
          category: d.expenseCategory,
          categoryName: d.categoryName,
          acctSubjectId: d.acctSubjectId,
          acctSubjectName: d.acctSubjectName,
          purpose: d.purpose,
          amount: d.totalAmount,
          taxRate: d.taxRate,
          projectId: d.projectId,
          projectName: d.projectName,
          costDeptId: d.costDeptId,
          costDeptName: d.costDeptName,
          customerVendorId: d.customerVendorId,
          customerVendorName: d.customerVendorName,
          approvedAmount: d.approvedAmount,
          bankName: d.bankName,
          bankAccountName: d.bankAccountName,
          bankAccount: d.bankAccount,
          remark: d.remark,
          attachmentPaths: d.attachments,
          sqMan: d.sqMan,
          sqManName: d.sqManName,
          aeNo: d.aeNo,
          aeDd: d.aeDd,
        );
      }
      FocusManager.instance.primaryFocus?.unfocus();
      final result = await ExpenseDetailDialog.show(
        context,
        api: ref.read(expenseApiProvider),
        l10n: l10n,
        initialData: initialData,
        checkAttachHealth: () =>
            ref.read(expenseApiProvider).checkAttachHealth(),
        showAttachments: false,
        canEditApprovedAmount: _canEditApprovedAmount,
        acctTree: _acctTree,
      );
      if (result != null && mounted) {
        final now = DateTime.now();
        final detail = ExpenseDetailModel(
          id: editIndex != null
              ? expense.details[editIndex].id
              : now.millisecondsSinceEpoch.toString(),
          expenseId: '',
          expenseCategory: result.category,
          categoryName: result.categoryName,
          purpose: result.purpose,
          amount: result.taxRate > 0
              ? result.amount / (1 + result.taxRate / 100)
              : result.amount,
          taxRate: result.taxRate,
          taxAmount: result.taxRate > 0
              ? result.amount - result.amount / (1 + result.taxRate / 100)
              : 0,
          totalAmount: result.amount,
          projectId: result.projectId,
          projectName: result.projectName,
          costDeptId: result.costDeptId,
          costDeptName: result.costDeptName,
          acctSubjectId: result.acctSubjectId,
          acctSubjectName: result.acctSubjectName,
          customerVendorId: result.customerVendorId,
          customerVendorName: result.customerVendorName,
          approvedAmount: result.approvedAmount,
          bankName: result.bankName,
          bankAccountName: result.bankAccountName,
          bankAccount: result.bankAccount,
          sqMan: result.sqMan,
          sqManName: result.sqManName,
          aeNo: result.aeNo,
          aeDd: result.aeDd,
          remark: result.remark,
          sortOrder: editIndex != null
              ? expense.details[editIndex].sortOrder
              : 1,
          preItm: editIndex != null ? expense.details[editIndex].preItm : null,
          attachments: result.attachmentPaths,
          createTime: now,
          updateTime: now,
        );
        if (editIndex != null) {
          _updateDetail(editIndex, detail);
        } else {
          _addDetail(detail);
        }
      }
    } finally {
      _addingDetail = false;
    }
  }

  void _showCurrencyPicker(String cur) {
    if (_currencies.isEmpty) {
      TDToast.showText(
        AppLocalizations.of(context).get('noData'),
        context: context,
      );
      return;
    }
    final l10n = AppLocalizations.of(context);
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final codes = _currencies.map((c) => c.curId).toList();
    final labels = _currencies.map((c) => '${c.curId}/${c.name}').toList();
    FocusManager.instance.primaryFocus?.unfocus();
    TDPicker.showMultiPicker(
      context,
      title: l10n.get('selectCurrency'),
      backgroundColor: colors.bgCard,
      data: [labels],
      onConfirm: (s) {
        if (s.isNotEmpty && s[0] is int) {
          final i = s[0] as int;
          if (i >= 0 && i < codes.length) {
            Navigator.of(context).pop();
            _updateCurrencyCode(codes[i]);
          }
        }
      },
    );
  }

  void _showTextInput(
    String title,
    Function(String) onConfirm, {
    String initialText = '',
  }) {
    FocusScope.of(context).unfocus();
    FocusManager.instance.primaryFocus?.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);
          },
        ),
      ),
    );
  }

  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,
              ),
            ),
        ],
      ),
    );
  }

  Widget _buildBottomButtons(AppLocalizations l10n) {
    return ActionBar(
      showLeft: false,
      showCenter: false,
      rightLabel: l10n.get('submit'),
      onRightTap: () async {
        if (_isSubmitting) return;
        final err = _validate(l10n);
        if (err.isNotEmpty) {
          TDToast.showText(err.first, context: context);
          return;
        }
        FocusScope.of(context).unfocus();
        _isSubmitting = true;
        LoadingDialog.show(context, text: l10n.get('submitting'));
        try {
          final data = _buildSubmitData();
          final api = ref.read(expenseApiProvider);
          await api.submit(data);
          if (mounted) {
            LoadingDialog.hide(context);
            TDToast.showSuccess(
              l10n.get('submitSuccess'),
              context: context,
            );
            WidgetsBinding.instance.addPostFrameCallback((_) {
              if (mounted) GoRouter.of(context).pop(true);
            });
          }
        } catch (e) {
          if (mounted) {
            LoadingDialog.hide(context);
            WidgetsBinding.instance.addPostFrameCallback((_) {
              if (mounted) _showSubmitError(e, l10n);
            });
          }
        } finally {
          _isSubmitting = false;
        }
      },
    );
  }

  void _showSubmitError(Object e, AppLocalizations l10n) {
    final message = _extractErrorMessage(e) ?? l10n.get('submitFailedRetry');
    showGeneralDialog(
      context: context,
      pageBuilder: (ctx, animation, secondaryAnimation) => TDConfirmDialog(
        title: l10n.get('submitFailed'),
        content: message,
        buttonStyle: TDDialogButtonStyle.text,
      ),
    );
  }

  String? _extractErrorMessage(Object e) {
    if (e is DioException) {
      if (e.error is ApiException) return (e.error as ApiException).message;
      if (e.error is NetworkException) {
        return (e.error as NetworkException).message;
      }
    }
    return null;
  }

  List<String> _validate(AppLocalizations l10n) {
    final e = <String>[];
    if (_purposeController.text.trim().isEmpty) {
      e.add(l10n.get('enterExpenseReason'));
    }
    if (_expense!.details.isEmpty) {
      e.add(l10n.get('addAtLeastOneDetail'));
    }
    return e;
  }

  bool _hasUnsaved() =>
      _purposeController.text.isNotEmpty ||
      (_expense?.paymentMethod.isNotEmpty ?? false) ||
      (_expense?.currencyCode.isNotEmpty ?? false) ||
      _remarkController.text.isNotEmpty ||
      (_expense?.details.isNotEmpty ?? false);

  void _doPop(AppLocalizations l10n) {
    if (_hasUnsaved()) {
      _showConfirmDialog(
        l10n.get('confirmExit'),
        l10n.get('unsavedContentWarning'),
        l10n.get('continueEditing'),
        l10n.get('discardAndExit'),
        () {
          if (!mounted) return;
          _forcePop();
        },
      );
    } else {
      _forcePop();
    }
  }

  void _forcePop() {
    FocusManager.instance.primaryFocus?.unfocus();
    final router = GoRouter.of(context);
    if (router.canPop()) {
      router.pop();
    } else {
      SystemNavigator.pop();
    }
  }

  void _showConfirmDialog(
    String title,
    String content,
    String leftText,
    String rightText,
    VoidCallback onConfirm,
  ) {
    FocusScope.of(context).unfocus();
    FocusManager.instance.primaryFocus?.unfocus();
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    showDialog(
      context: context,
      useRootNavigator: true,
      builder: (ctx) => TDAlertDialog(
        title: title,
        content: content,
        buttonStyle: TDDialogButtonStyle.text,
        leftBtn: TDDialogButtonOptions(
          title: leftText,
          titleColor: colors.primary,
          action: () => Navigator.pop(ctx),
        ),
        rightBtn: TDDialogButtonOptions(
          title: rightText,
          titleColor: colors.danger,
          action: () {
            Navigator.pop(ctx);
            onConfirm();
          },
        ),
      ),
    );
  }

  Widget _buildPageFooter() {
    final l10n = AppLocalizations.of(context);
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return Center(
      child: Padding(
        padding: const EdgeInsets.only(bottom: 16),
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(
              Icons.rocket_launch_outlined,
              size: 16,
              color: colors.textPlaceholder,
            ),
            const SizedBox(width: 6),
            Text(
              l10n.get('pageFooter'),
              style: TextStyle(
                fontSize: AppFontSizes.caption,
                color: colors.textPlaceholder,
              ),
            ),
          ],
        ),
      ),
    );
  }

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