import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:dio/dio.dart';

import '../../core/i18n/app_localizations.dart';
import '../../core/network/api_exception.dart';
import '../../core/navigation/host_app_channel.dart';
import '../../core/theme/app_colors.dart';
import '../../core/theme/app_colors_extension.dart';
import '../../shared/widgets/action_bar.dart';
import '../../shared/widgets/app_skeletons.dart';
import '../../shared/widgets/form_field_row.dart';
import '../../shared/widgets/form_section.dart';
import '../../shared/widgets/loading_dialog.dart';
import '../../shared/widgets/nav_bar_config.dart';
import '../../shared/widgets/app_input_dialog.dart';
import '../../shared/widgets/searchable_picker_sheet.dart';
import 'vehicle_apply_api.dart';
import 'vehicle_apply_list_controller.dart';
import 'vehicle_apply_model.dart';

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

  @override
  ConsumerState<VehicleApplyEditPage> createState() =>
      _VehicleApplyEditPageState();
}

class _VehicleApplyEditPageState extends ConsumerState<VehicleApplyEditPage> {
  // ── 原单数据 ──
  String _billNo = '';
  String _applyDate = '';

  // ── 基本信息 ──
  final _reasonController = TextEditingController();
  final _reasonFocus = FocusNode();
  String _selectedApplicantId = '';
  String _selectedApplicantName = '';

  // ── 行程信息 ──
  String _originAdr = '';
  String _destAdr = '';
  DateTime? _startTime;
  DateTime? _endTime;

  // ── 车辆信息 ──
  String _licensePlate = '';
  String _vehicleType = '';
  String _brand = '';
  int _seats = 0;
  int _odometerBegin = 0;

  // ── 驾驶员 ──
  String _driverName = '';

  // ── 同行信息 ──
  final List<EmployeeItem> _passengers = [];

  // ── 参考数据 ──
  List<DepartmentItem> _departments = [];
  List<EmployeeItem> _employees = [];
  bool _firstBuild = true;
  bool _refDataLoading = true;
  bool _loadingBill = true;
  String? _loadingError;
  String _selectedDeptId = '';
  String _selectedDeptName = '';

  final _scrollCtrl = ScrollController();

  // Mock 车辆类型
  static const _mockVehicleTypes = [
    'sedan',
    'suv',
    'mpv',
    'van',
    'truck',
    'pickup',
    'minibus',
    'bus',
  ];

  @override
  void initState() {
    super.initState();
    SystemChrome.setSystemUIOverlayStyle(
      const SystemUiOverlayStyle(
        statusBarColor: Colors.transparent,
        statusBarIconBrightness: Brightness.dark,
      ),
    );
    _reasonFocus.addListener(() => _ensureVisible(_reasonFocus));
    _departments = [];
    _refDataLoading = true;
    _loadingBill = true;
    _loadRefData();
    _loadBillData();
    WidgetsBinding.instance.addPostFrameCallback((_) => _checkDataReady());
  }

  void _checkDataReady() {
    if (!_refDataLoading && !_loadingBill && mounted) {
      setState(() => _firstBuild = false);
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) setState(() {});
      });
    } else if (mounted) {
      WidgetsBinding.instance.addPostFrameCallback((_) => _checkDataReady());
    }
  }

  Future<void>? _refDataFuture;

  Future<void> _loadRefData({bool showLoading = false}) async {
    if (_refDataFuture != null) return _refDataFuture!;
    final completer = Completer<void>();
    _refDataFuture = completer.future;
    if (showLoading) {
      LoadingDialog.show(
        context,
        text: AppLocalizations.of(context).get('dataLoading'),
      );
    }
    try {
      final api = ref.read(vehicleApplyApiProvider);
      final results = await Future.wait([
        api.getDepartments(),
        api.getEmployees(),
      ]);
      if (!mounted) return;
      setState(() {
        _departments = results[0] as List<DepartmentItem>;
        _employees = results[1] as List<EmployeeItem>;
        _refDataLoading = false;
        _autoSelectDept();
        _autoSelectApplicant();
        _autoSelectPassenger();
        _autoSelectDriver();
        if (!_loadingBill) {
          _resolvePassengersFromEmployees();
        }
      });
      completer.complete();
    } catch (_) {
      if (!mounted) {
        completer.complete();
        return;
      }
      setState(() => _refDataLoading = false);
      completer.complete();
    } finally {
      if (showLoading && mounted) LoadingDialog.hide(context);
      _refDataFuture = null;
    }
  }

  void _autoSelectDept() {
    if (_selectedDeptId.isNotEmpty) return;
    final dep = HostAppChannel.dep;
    if (dep.isEmpty) return;
    final match = _departments.where((d) => d.dep == dep);
    if (match.isNotEmpty) {
      _selectedDeptId = match.first.dep;
      _selectedDeptName = match.first.name;
    }
  }

  void _autoSelectApplicant() {
    if (_selectedApplicantId.isNotEmpty) return;
    final usr = HostAppChannel.usr;
    if (usr.isEmpty) return;
    final match = _employees.where((e) => e.salNo == usr);
    if (match.isNotEmpty) {
      _selectedApplicantId = match.first.salNo;
      _selectedApplicantName = match.first.name;
    }
  }

  void _autoSelectPassenger() {
    final usr = HostAppChannel.usr;
    if (usr.isEmpty) return;
    final match = _employees.where((e) => e.salNo == usr);
    if (match.isNotEmpty && !_passengers.any((p) => p.salNo == usr)) {
      _passengers.add(match.first);
    }
  }

  void _autoSelectDriver() {
    if (_driverName.isNotEmpty) return;
    final usr = HostAppChannel.usr;
    if (usr.isEmpty) return;
    final match = _employees.where((e) => e.salNo == usr);
    if (match.isNotEmpty) {
      _driverName = match.first.name;
    }
  }

  void _resolvePassengersFromEmployees() {
    for (int i = 0; i < _passengers.length; i++) {
      final p = _passengers[i];
      if (p.salNo.isEmpty) {
        final match = _employees.where((e) => e.name == p.name);
        if (match.isNotEmpty) {
          _passengers[i] = match.first;
        }
      }
    }
  }

  Future<void> _loadBillData() async {
    try {
      // 先用 mock 数据匹配
      final match = mockVehicles.where((e) => e.ycNo == widget.billNo);
      if (match.isNotEmpty) {
        _fillFromModel(match.first);
        if (mounted) setState(() => _loadingBill = false);
        return;
      }

      // 尝试 API
      final api = ref.read(vehicleApplyApiProvider);
      final detail = await api.fetchDetail(widget.billNo);
      if (!mounted) return;
      _fillFromModel(detail);
      setState(() => _loadingBill = false);
    } catch (e) {
      if (!mounted) return;
      setState(() {
        _loadingBill = false;
        _loadingError = e.toString();
      });
    }
  }

  void _fillFromModel(VehicleApplyModel model) {
    _billNo = model.ycNo;
    _applyDate = model.ycDd != null
        ? '${model.ycDd!.year}-${model.ycDd!.month.toString().padLeft(2, '0')}-${model.ycDd!.day.toString().padLeft(2, '0')}'
        : '';
    _selectedDeptId = model.dep;
    _selectedDeptName = model.deptName;
    _selectedApplicantId = model.salNo;
    _selectedApplicantName = model.applicantName.isNotEmpty
        ? model.applicantName
        : model.salName;
    _reasonController.text = model.reason;
    _originAdr = model.originAdr;
    _destAdr = model.destAdr;
    _startTime = model.startTime;
    _endTime = model.endTime;
    _licensePlate = model.licensePlate;
    _vehicleType = model.vehicleType;
    _brand = model.brand;
    _seats = model.seats;
    _odometerBegin = model.odometerBegin;
    _driverName = model.driverName;
    _initPassengersFromModel(model);
  }

  void _initPassengersFromModel(VehicleApplyModel model) {
    _passengers.clear();
    if (model.passengerName.isNotEmpty) {
      final segments = model.passengerName.split(';');
      for (final seg in segments) {
        final trimmed = seg.trim();
        if (trimmed.isEmpty) continue;
        // 格式：salNo/name 或纯 name（兼容旧数据）
        final parts = trimmed.split('/');
        if (parts.length >= 2) {
          final salNo = parts[0].trim();
          final name = parts.sublist(1).join('/').trim();
          final match = _employees.where((e) => e.salNo == salNo);
          _passengers.add(
            match.isNotEmpty
                ? match.first
                : EmployeeItem(salNo: salNo, name: name),
          );
        } else {
          // 旧格式：纯姓名，按姓名匹配
          final match = _employees.where(
            (e) => e.name == trimmed || e.salNo == trimmed,
          );
          if (match.isNotEmpty) {
            _passengers.add(match.first);
          } else {
            _passengers.add(EmployeeItem(salNo: '', name: trimmed));
          }
        }
      }
    }
  }

  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() {
    _reasonController.dispose();
    _reasonFocus.dispose();
    _scrollCtrl.dispose();
    super.dispose();
  }

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

    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;
                  _loadingBill = true;
                });
                _loadBillData();
              },
            ),
          ],
        ),
      );
    }

    if (_firstBuild) {
      return const SkeletonFormPage(
        sectionRows: [4, 6, 4, 1],
        bottomButtonCount: 1,
      );
    }

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

    return PopScope(
      canPop: false,
      onPopInvokedWithResult: (didPop, _) {
        if (didPop) return;
        _doPop();
      },
      child: Column(
        children: [
          Expanded(
            child: GestureDetector(
              onTap: () => FocusScope.of(context).unfocus(),
              child: SingleChildScrollView(
                controller: _scrollCtrl,
                padding: const EdgeInsets.all(16),
                child: Column(
                  children: [
                    _buildBasicInfo(l10n),
                    const SizedBox(height: 16),
                    _buildVehicleInfo(l10n),
                    const SizedBox(height: 16),
                    _buildTripInfo(l10n),
                    const SizedBox(height: 16),
                    _buildPassengerInfo(l10n),
                    const SizedBox(height: 24),
                    _buildPageFooter(),
                  ],
                ),
              ),
            ),
          ),
          _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('vehicleApplyNo'),
          value: _billNo,
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('date'),
          value: _applyDate,
          readOnly: true,
          showArrow: false,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('dep'),
          value: _selectedDeptId.isNotEmpty
              ? '$_selectedDeptId/$_selectedDeptName'
              : '',
          hint: l10n.get('pleaseSelect'),
          onTap: _refDataLoading ? null : () => _showDeptPicker(),
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('applicant'),
          value: _selectedApplicantId.isNotEmpty
              ? '$_selectedApplicantId/$_selectedApplicantName'
              : '',
          hint: l10n.get('pleaseSelect'),
          onTap: () => _showApplicantPicker(),
        ),
        const SizedBox(height: 16),
        _label(l10n.get('vehicleReason'), required: true),
        const SizedBox(height: 8),
        TDTextarea(
          controller: _reasonController,
          focusNode: _reasonFocus,
          hintText: l10n.get('enterVehicleReason'),
          maxLines: 4,
          minLines: 1,
          maxLength: 500,
          indicator: true,
          padding: EdgeInsets.zero,
          bordered: true,
          backgroundColor: colors.bgPage,
        ),
      ],
    );
  }

  // ═══ 2. 车辆信息 ═══
  Widget _buildVehicleInfo(AppLocalizations l10n) {
    return FormSection(
      title: l10n.get('vehicleInfo'),
      leadingIcon: Icons.directions_car_outlined,
      children: [
        FormFieldRow(
          label: l10n.get('licensePlate'),
          value: _licensePlate.isNotEmpty ? _licensePlate : null,
          hint: l10n.get('pleaseEnter'),
          required: true,
          onTap: () => _showTextInput(
            l10n.get('licensePlate'),
            (v) => setState(() => _licensePlate = v),
            initialText: _licensePlate,
          ),
          onClear: _licensePlate.isNotEmpty
              ? () => setState(() => _licensePlate = '')
              : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('vehicleType'),
          value: _vehicleType.isNotEmpty
              ? _vehicleTypeLabel(_vehicleType, l10n)
              : null,
          hint: l10n.get('pleaseSelect'),
          onTap: () => _showVehicleTypePicker(l10n),
          onClear: _vehicleType.isNotEmpty
              ? () => setState(() => _vehicleType = '')
              : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('brand'),
          value: _brand.isNotEmpty ? _brand : null,
          hint: l10n.get('pleaseEnter'),
          onTap: () => _showTextInput(
            l10n.get('brand'),
            (v) => setState(() => _brand = v),
            initialText: _brand,
          ),
          onClear: _brand.isNotEmpty ? () => setState(() => _brand = '') : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('seats'),
          value: _seats > 0 ? '$_seats' : null,
          hint: l10n.get('pleaseEnter'),
          onTap: () => _showNumberInput(
            l10n.get('seats'),
            (v) => setState(() => _seats = v),
            _seats,
            min: 1,
          ),
          onClear: _seats > 0 ? () => setState(() => _seats = 0) : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('odometerBegin'),
          value: _odometerBegin > 0 ? '$_odometerBegin' : null,
          hint: l10n.get('pleaseEnter'),
          onTap: () => _showNumberInput(
            l10n.get('odometerBegin'),
            (v) => setState(() => _odometerBegin = v),
            _odometerBegin,
          ),
          onClear: _odometerBegin > 0
              ? () => setState(() => _odometerBegin = 0)
              : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('driverName'),
          value: _driverName.isNotEmpty ? _driverName : null,
          hint: l10n.get('pleaseSelect'),
          onTap: () => _showDriverPicker(),
          onClear: _driverName.isNotEmpty
              ? () => setState(() => _driverName = '')
              : null,
        ),
      ],
    );
  }

  // ═══ 3. 行程信息 ═══
  Widget _buildTripInfo(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    final timeError =
        _startTime != null &&
        _endTime != null &&
        !_endTime!.isAfter(_startTime!);
    return FormSection(
      title: l10n.get('tripInfo'),
      leadingIcon: Icons.route_outlined,
      children: [
        FormFieldRow(
          label: l10n.get('departTime'),
          value: _startTime != null ? _formatDateTime(_startTime!) : null,
          hint: l10n.get('pleaseSelect'),
          required: true,
          onTap: () =>
              _pickDateTime((d) => setState(() => _startTime = d), _startTime),
          onClear: _startTime != null
              ? () => setState(() => _startTime = null)
              : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('returnTime'),
          value: _endTime != null ? _formatDateTime(_endTime!) : null,
          hint: l10n.get('pleaseSelect'),
          required: true,
          onTap: () =>
              _pickDateTime((d) => setState(() => _endTime = d), _endTime),
          onClear: _endTime != null
              ? () => setState(() => _endTime = null)
              : null,
        ),
        if (timeError)
          Padding(
            padding: const EdgeInsets.only(top: 8),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.end,
              children: [
                Icon(
                  Icons.warning_amber_rounded,
                  size: 14,
                  color: colors.danger,
                ),
                const SizedBox(width: 4),
                Text(
                  l10n.get('returnTimeMustLater'),
                  style: TextStyle(
                    fontSize: AppFontSizes.caption,
                    color: colors.danger,
                  ),
                ),
              ],
            ),
          ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('origin'),
          value: _originAdr.isNotEmpty ? _originAdr : null,
          hint: l10n.get('pleaseEnter'),
          onTap: () => _showTextInput(
            l10n.get('origin'),
            (v) => setState(() => _originAdr = v),
            initialText: _originAdr,
          ),
          onClear: _originAdr.isNotEmpty
              ? () => setState(() => _originAdr = '')
              : null,
        ),
        const SizedBox(height: 16),
        FormFieldRow(
          label: l10n.get('destination'),
          value: _destAdr.isNotEmpty ? _destAdr : null,
          hint: l10n.get('pleaseEnter'),
          onTap: () => _showTextInput(
            l10n.get('destination'),
            (v) => setState(() => _destAdr = v),
            initialText: _destAdr,
          ),
          onClear: _destAdr.isNotEmpty
              ? () => setState(() => _destAdr = '')
              : null,
        ),
      ],
    );
  }

  // ═══ 4. 同行信息 ═══
  Widget _buildPassengerInfo(AppLocalizations l10n) {
    final colors = Theme.of(context).extension<AppColorsExtension>()!;
    return FormSection(
      title: l10n.get('companionInfo'),
      leadingIcon: Icons.people_outline,
      showAction: true,
      actionText: l10n.get('add'),
      onActionTap: () => _showMultiEmployeePicker(l10n),
      children: [
        if (_passengers.isEmpty)
          Padding(
            padding: const EdgeInsets.symmetric(vertical: 8),
            child: Text(
              l10n.get('noPassengerHint'),
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                color: colors.textPlaceholder,
              ),
            ),
          )
        else
          Wrap(
            spacing: 10,
            runSpacing: 10,
            children: _passengers.map((p) {
              final label =
                  '${p.salNo}/${p.name.isNotEmpty ? p.name : p.salNo}';
              return Container(
                padding: const EdgeInsets.symmetric(
                  horizontal: 14,
                  vertical: 10,
                ),
                decoration: BoxDecoration(
                  color: colors.bgCard,
                  borderRadius: BorderRadius.circular(20),
                  border: Border.all(color: colors.border, width: 0.5),
                  boxShadow: [
                    BoxShadow(
                      color: Colors.black.withValues(alpha: 0.04),
                      blurRadius: 4,
                      offset: const Offset(0, 1),
                    ),
                  ],
                ),
                child: Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(Icons.person_outline, size: 16, color: colors.primary),
                    const SizedBox(width: 8),
                    Text(
                      label,
                      style: TextStyle(
                        fontSize: AppFontSizes.body,
                        color: colors.textPrimary,
                      ),
                    ),
                    const SizedBox(width: 8),
                    GestureDetector(
                      onTap: () {
                        setState(() {
                          _passengers.removeWhere((e) => e.salNo == p.salNo);
                        });
                      },
                      child: Container(
                        width: 20,
                        height: 20,
                        decoration: BoxDecoration(
                          color: colors.bgPage,
                          shape: BoxShape.circle,
                        ),
                        child: Icon(
                          Icons.close,
                          size: 12,
                          color: colors.textSecondary,
                        ),
                      ),
                    ),
                  ],
                ),
              );
            }).toList(),
          ),
        const SizedBox(height: 16),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceBetween,
          children: [
            Text(
              l10n.get('passengerCount'),
              style: TextStyle(
                fontSize: AppFontSizes.body,
                fontWeight: FontWeight.w600,
                color: colors.textPrimary,
              ),
            ),
            Text(
              '${_passengers.length}',
              style: TextStyle(
                fontSize: AppFontSizes.subtitle,
                fontWeight: FontWeight.w700,
                color: colors.textPrimary,
              ),
            ),
          ],
        ),
      ],
    );
  }

  // ═══ 5. 底部操作栏 ═══
  Widget _buildBottomBar(AppLocalizations l10n) {
    return ActionBar(
      showLeft: false,
      showCenter: false,
      rightLabel: l10n.get('submit'),
      onRightTap: () async {
        final err = _validate(l10n);
        if (err.isNotEmpty) {
          TDToast.showText(err.first, context: context);
          return;
        }
        FocusScope.of(context).unfocus();
        LoadingDialog.show(context, text: l10n.get('submitting'));
        try {
          final data = _buildSubmitData();
          final api = ref.read(vehicleApplyApiProvider);
          final billNo = await api.submit(data);
          if (mounted) {
            LoadingDialog.hide(context);
            TDToast.showSuccess(l10n.get('submitSuccess'), context: context);
            // BillSave 成功后异步提交审核流，不阻塞不弹窗
            if (billNo != null) {
              api.shSubmit(
                bilId: 'YC',
                bilNo: billNo,
                bilDd: data['HeadData']['YC_DD']?.toString() ?? '',
              );
            }
            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);
            });
          }
        }
      },
    );
  }

  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;
  }

  Map<String, dynamic> _buildSubmitData() {
    return {
      'HeadData': {
        'YC_NO': _billNo,
        'YC_DD': _applyDate,
        'SAL_NO': _selectedApplicantId.isNotEmpty
            ? _selectedApplicantId
            : HostAppChannel.usr,
        'DEP': _selectedDeptId,
        'REASON': _reasonController.text.trim(),
        'ORIGIN_ADR': _originAdr,
        'DEST_ADR': _destAdr,
        'PASSENGER_COUNT': _passengers.length,
        'PASSENGER_NAME': _passengers
            .map((p) => '${p.salNo}/${p.name.isNotEmpty ? p.name : p.salNo}')
            .join(';'),
        'LICENSEPLATE': _licensePlate,
        'VEHICLE_TYPE': _vehicleType,
        'BRAND': _brand,
        'ODOMETER_BEGIN': _odometerBegin,
        'SEATS': _seats,
        'DRIVER_NAME': _driverName,
        'START_TIME': _startTime?.toIso8601String(),
        'END_TIME': _endTime?.toIso8601String(),
        'USR': HostAppChannel.usr,
      },
    };
  }

  List<String> _validate(AppLocalizations l10n) {
    final e = <String>[];
    if (_reasonController.text.trim().isEmpty) {
      e.add(l10n.get('enterVehicleReason'));
    }
    if (_licensePlate.isEmpty) e.add(l10n.get('selectLicensePlateHint'));
    if (_startTime == null) {
      e.add(l10n.get('selectDepartTime'));
    }
    if (_endTime == null) {
      e.add(l10n.get('selectReturnTime'));
    }
    if (_startTime != null &&
        _endTime != null &&
        !_endTime!.isAfter(_startTime!)) {
      e.add(l10n.get('returnTimeMustLater'));
    }
    return e;
  }

  // ═══ 弹窗方法 ═══
  Future<void> _showTextInput(
    String title,
    void Function(String) onConfirm, {
    String initialText = '',
  }) async {
    FocusScope.of(context).unfocus();
    FocusManager.instance.primaryFocus?.unfocus();
    final result = await AppInputDialog.show(
      context: context,
      title: title,
      initialText: initialText,
    );
    if (result != null && mounted) {
      onConfirm(result);
    }
  }

  Future<void> _showNumberInput(
    String title,
    void Function(int) onSave,
    int current, {
    int min = 0,
  }) async {
    FocusScope.of(context).unfocus();
    FocusManager.instance.primaryFocus?.unfocus();
    final result = await AppInputDialog.show(
      context: context,
      title: title,
      initialText: current > 0 ? '$current' : '',
      inputType: AppInputType.integer,
      min: min,
    );
    if (result != null && mounted) {
      onSave(int.tryParse(result) ?? 0);
    }
  }

  void _pickDateTime(void Function(DateTime) onPicked, DateTime? initial) {
    FocusScope.of(context).unfocus();
    final l10n = AppLocalizations.of(context);
    final now = DateTime.now();
    final d = initial ?? now;
    TDPicker.showDatePicker(
      context,
      title: l10n.get('selectDateTime'),
      useYear: true,
      useMonth: true,
      useDay: true,
      useHour: true,
      useMinute: true,
      dateStart: [now.year - 1, now.month, now.day, now.hour, now.minute],
      dateEnd: [now.year + 10, 12, 31, 23, 59],
      initialDate: [d.year, d.month, d.day, d.hour, d.minute],
      onConfirm: (selected) {
        Navigator.of(context).pop();
        onPicked(
          DateTime(
            selected['year']!,
            selected['month']!,
            selected['day']!,
            selected['hour']!,
            selected['minute']!,
          ),
        );
      },
    );
  }

  Future<void> _showApplicantPicker() async {
    FocusScope.of(context).unfocus();
    FocusManager.instance.primaryFocus?.unfocus();
    final l10n = AppLocalizations.of(context);
    final api = ref.read(vehicleApplyApiProvider);
    final result = await showSearchablePicker<EmployeeItem>(
      context,
      title: '${l10n.get('select')}${l10n.get('applicant')}',
      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}',
    );
    if (result != null && mounted) {
      setState(() {
        _selectedApplicantId = result.salNo;
        _selectedApplicantName = result.name;
      });
    }
  }

  Future<void> _showDeptPicker() async {
    FocusManager.instance.primaryFocus?.unfocus();
    final l10n = AppLocalizations.of(context);
    final api = ref.read(vehicleApplyApiProvider);
    final result = await showSearchablePicker<DepartmentItem>(
      context,
      title: '${l10n.get('select')}${l10n.get('applyDept')}',
      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}',
    );
    if (result != null && mounted) {
      setState(() {
        _selectedDeptId = result.dep;
        _selectedDeptName = result.name;
      });
    }
  }

  void _showVehicleTypePicker(AppLocalizations l10n) {
    FocusScope.of(context).unfocus();
    final labels = _mockVehicleTypes
        .map((t) => _vehicleTypeLabel(t, l10n))
        .toList();
    TDPicker.showMultiPicker(
      context,
      title: l10n.get('selectVehicleType'),
      data: [labels],
      onConfirm: (selected) {
        final idx = labels.indexOf(selected.first);
        if (idx >= 0) setState(() => _vehicleType = _mockVehicleTypes[idx]);
      },
    );
  }

  Future<void> _showMultiEmployeePicker(AppLocalizations l10n) async {
    FocusScope.of(context).unfocus();
    FocusManager.instance.primaryFocus?.unfocus();
    final api = ref.read(vehicleApplyApiProvider);
    final selected = await showSearchableMultiPicker<EmployeeItem>(
      context,
      title: '${l10n.get('select')}${l10n.get('companion')}',
      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}',
    );
    if (mounted) {
      setState(() {
        for (final emp in selected) {
          if (!_passengers.any((p) => p.salNo == emp.salNo)) {
            _passengers.add(emp);
          }
        }
      });
    }
  }

  Future<void> _showDriverPicker() async {
    FocusManager.instance.primaryFocus?.unfocus();
    final l10n = AppLocalizations.of(context);
    final api = ref.read(vehicleApplyApiProvider);
    final result = await showSearchablePicker<EmployeeItem>(
      context,
      title: '${l10n.get('select')}${l10n.get('driverName')}',
      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}',
    );
    if (result != null && mounted) {
      setState(() {
        _driverName = result.name;
      });
    }
  }

  // ═══ 对话框 ═══
  void _doPop() {
    if (_hasUnsaved()) {
      final l10n = AppLocalizations.of(context);
      _showConfirmDialog(
        l10n.get('confirmExit'),
        l10n.get('unsavedContentWarning'),
        l10n.get('continueEditing'),
        l10n.get('discardAndExit'),
        _forcePop,
      );
    } else {
      _forcePop();
    }
  }

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

  bool _hasUnsaved() {
    final usr = HostAppChannel.usr;
    final hasCustomPassengers = _passengers.any((p) => p.salNo != usr);
    final selfEmployee = _employees.where((e) => e.salNo == usr);
    final isDefaultDriver =
        selfEmployee.isNotEmpty && _driverName == selfEmployee.first.name;

    return _reasonController.text.isNotEmpty ||
        _originAdr.isNotEmpty ||
        _destAdr.isNotEmpty ||
        _startTime != null ||
        _endTime != null ||
        _licensePlate.isNotEmpty ||
        _vehicleType.isNotEmpty ||
        _brand.isNotEmpty ||
        _seats > 0 ||
        (_driverName.isNotEmpty && !isDefaultDriver) ||
        hasCustomPassengers ||
        _odometerBegin > 0;
  }

  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 _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 _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 _formatDateTime(DateTime d) {
    return '${d.year}-${d.month.toString().padLeft(2, '0')}-${d.day.toString().padLeft(2, '0')} ${d.hour.toString().padLeft(2, '0')}:${d.minute.toString().padLeft(2, '0')}';
  }

  String _vehicleTypeLabel(String key, AppLocalizations l10n) {
    switch (key) {
      case 'sedan':
        return l10n.get('sedan');
      case 'suv':
        return 'SUV';
      case 'mpv':
        return l10n.get('businessVan');
      case 'van':
        return l10n.get('van');
      case 'truck':
        return l10n.get('truck');
      case 'pickup':
        return l10n.get('pickup');
      case 'minibus':
        return l10n.get('minibus');
      case 'bus':
        return l10n.get('bus');
      default:
        return key;
    }
  }
}
